7

Apache/Nginx伪静态规则匹配http://出现的问题与解决

 3 years ago
source link: https://zhang.ge/5020.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
WEB应用

Apache/Nginx伪静态规则匹配http://出现的问题与解决

Jager · 3月3日 · 2015年nginx配置 · 伪静态 · 静态缓存 380次已读

这个问题不知道有没有人遇到过,反正度娘和谷姐都没能帮到我!困扰了我挺长时间了,今天偶尔将代码放到Apache服务器下测试时,意外解决了!

问题是这样的,我搭建了一个网站icon图标抓取的API接口,正常情况下对象的传参是通过$_GET['url']获取的,因此常规获取图标的地址应该是:

http://domain.com/?url=zhang.ge

http://domain.com/?url=https://zhang.ge

为了开启浏览器缓存和后续的CDN缓存,我的设计思路如下:

①、在图标API网站目录下新建一个cache文件夹,以域名.ico的形式保存图标文件,比如zhang.ge.ico

②、当抓取某个网站的ico时,先通过Nginx或Apache判断是否存在缓存文件,如果存在就直接返回给浏览器,这样在没开启CDN的情况下,因为返回的是纯静态文件,浏览器将会自动缓存,也就是返回304状态,加载速度得到提升!

为了开启浏览器缓存,我将地址如下伪静态化:

http://domain.com/zhang.ge

http://domain.com/https://zhang.ge

这是之前写的Nginx下的伪静态规则:

#将包含http://的请求重写,去掉其中的http://,省去php代码的动态判断
rewrite ^/http://(.*)$ /cache/$1.ico last;
#以下判断主要是为了避免API首页的元素一同被伪静态了(最后用与逻辑判断$type = abc即可)!
set $type '';
if ( !-f $request_filename ){ #为了不和API页面上的静态资源冲突,排除已存在的文件请求
set $type a;
if ( $request_uri !~ (\.|/)$){ #不匹配含 . 或以/结尾的请求,为了兼容首页[/]请求;
set $type '${type}b';
if ( $request_uri !~ cache ){ #为了不和第一条规则冲突,不匹配含有cache的请求
set $type '${type}c';
#nginx不支持多重条件一同判断,所以先分开判断得到flag,最后合并判断即可:
if ( $type = abc ) {
#将条件外的其他所有请求重写到 cache/域名.ico
rewrite ^/(.*)$ /cache/$1.ico last;
#如果请求的文件已存在,则直接返回给用户,不再通过PHP
if (-f $request_filename) {
break;
#如果请求的文件不存在,则交给index.php处理
rewrite ^/cache/(.*).ico$ /index.php?url=$1 last;

当时发现不能生效!怎么都匹配不到http://,最后无奈只好用php重写参数中http://了!

今天,我将这个图标API搬家到了万网的免费主机上,是Apache环境,于是按照nginx的规则又写了一遍:

RewriteEngine on
RewriteBase /
#重写去掉请求中的"http://"
RewriteRule ^http://(.*)$ /cache/$1.ico [L]
#和nginx一致的条件判断,为了避免API首页被伪静态
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !(\.|/)$
RewriteCond %{REQUEST_URI} !cache
#将条件之外的其他请求全部重写到/cache/域名.ico
RewriteRule ^(.*)$ /cache/$1.ico [L]
#若文件不存在,则丢给index.php处理
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /index.php?url=$1 [L]

依然不行!奇了怪了,怎么就不能匹配http://呢?于是各种测试,比如将冒号和斜杠缓存url编码都不行!

其实在用nginx失败之后,我用php获取$_GET['url']发现得到的参数中的http://会是http:/,少一个斜杠!而且直接使用http://domain.com/?url=https://zhang.ge获取也是http:/zhang.ge,少一个斜杠!

今天鬼使神差的试了下伪静态中判断http:/,结果成功了!我擦原来要匹配http://,实际上是匹配http:/,少一个斜杠!真实匪夷所思,以前从来没遇到过!

所以上述2个伪静态规则应该如下编写:

A. Nginx伪静态:

#将包含http://的请求重写,去掉其中的http://,省去php代码的动态判断(实际上是匹配http:/)
rewrite ^/http:/(.*)$ /cache/$1.ico last;
#以下判断主要是为了避免API首页的元素一同被伪静态了!
if ( -f $request_filename ){ #为了不和API页面上的静态资源冲突
set $type 1;
if ( $request_uri ~ (\.|/)$){ #为了不和API首页冲突,即 / 这个请求
set $type 1;
if ( $request_uri ~ cache ){ #为了不和第一条规则冲突
set $type 1;
#nginx不支持多重条件一同判断,所以分开写。
if ( $type != 1 ) {
#将条件外的其他所有请求重写到 cache/域名.ico
rewrite ^/(.*)$ /cache/$1.ico last;
#如果请求的文件已存在,则直接返回给用户,不再通过PHP
if (-f $request_filename) {
break;
#如果请求的文件不存在,则交给index.php处理
rewrite ^/cache/(.*).ico$ /index.php?url=$1 last;

 B. Apache伪静态:

RewriteEngine on
RewriteBase /
#重写去掉请求中的"http://",实际上是匹配http:/
RewriteRule ^http:/(.*)$ /cache/$1.ico [L]
#和nginx一致的条件判断,为了避免API首页被伪静态
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !(\.|/)$
RewriteCond %{REQUEST_URI} !cache
#将条件之外的其他请求全部重写到/cache/域名.ico
RewriteRule ^(.*)$ /cache/$1.ico [L]
#若文件不存在,则丢给index.php处理
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /index.php?url=$1 [L]

文章写的很啰嗦,实际上关键性解释就是,在Nginx或Apache中要匹配请求url中的【http://】,应该是匹配【http:/】,也就是少写一个斜杠!大胆猜测匹配其他多个斜杠也应该是少一个斜杠。。。

好了,文章洋洋洒洒写了这么多,网站图标API也是成功搭建在万网免费虚拟主机上了。地址是http://seo.zgboke.com/geticon/ ,虽然是专门给中国博客联盟用的,但是如果你有图标调用需求,也可以在合理使用的前提下自由发挥。

另外,要查看是否实现浏览器缓存很简单,随便访问一个ico地址,比如:

http://seo.zgboke.com/geticon/zhang.ge

然后按下F12进入开发模式,定位到network(网络选项卡),多刷新一次就能看到304状态了:

304表示当前文件来自浏览器缓存,因为请求的文件和服务段的文件一致,不需要重复调取!

当然,本文写到的伪静态规则只是一部分,如果要实现CDN加速,那还得新增相应的规则,不过这都是后话了,等下次我在张戈博客分享这个网站图标抓取API源码的时候,会一并贴上,敬请期待!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK