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

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

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

http://domain.com/?url=zhangge.net 或 http://domain.com/?url=http://zhangge.net

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

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

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

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

http://domain.com/zhangge.net 或 http://domain.com/http://zhangge.net

这是之前写的 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=http://zhangge.net 获取也是 http:/zhangge.net,少一个斜杠!

今天鬼使神差的试了下伪静态中判断 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/zhangge.net

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

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

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java架构

Java并发之Condition的实现分析

回忆 synchronized 关键字,它配合 Object 的 wait()、notify() 系列方法可以实现等待/通知模式。

8220
来自专栏青玉伏案

iOS开发之再探多线程编程:Grand Central Dispatch详解

Swift3.0相关代码已在github上更新。之前关于iOS开发多线程的内容发布过一篇博客,其中介绍了NSThread、操作队列以及GCD,介绍的不够深入。今...

20270
来自专栏IMWeb前端团队

上手 yeoman generator

最近折腾脚手架相关的一些事情。说到脚手架,不得不谈的就是yeoman了。 是什么 yeoman是一个脚手架生成工具。 yeoman generator则是yeo...

24050
来自专栏风中追风

java类的加载过程和类加载器的分析

我们知道,我们写的java代码保存的格式是 .java, java文件被编译后会转换为字节码,字节码可以在任何平台通过java虚拟机来运行,这也是java能够跨...

55780
来自专栏云计算教程系列

如何在Ubuntu 14.04上为Apache设置mod_rewrite

在本教程中,我们将激活并学习如何使用Apache2 mod_rewrite模块管理URL重写。这个工具允许我们以更干净的方式重写URL,将人类可读的路径转换为代...

6000
来自专栏王亚昌的专栏

对数据操作封装的一点心得

在对数据进行操作时,可能需要读写name,于是我们写了一个接口,这个接口会实时更新缓存

7710
来自专栏芋道源码1024

分布式消息队列 RocketMQ 源码分析 —— Message 拉取与消费(上)

摘要: 原创出处 http://www.iocoder.cn/RocketMQ/message-pull-and-consume-first/ 「芋道源码」欢迎...

38160
来自专栏知识分享

Unexpected namespace prefix "xmlns" found for tag Linear Layout

原文地址http://blog.csdn.net/taxuexumei/article/details/41523419 今天遇到的问题,,,保存到博客里,下回...

39280
来自专栏熊训德的专栏

Hbase compaction 源码分析一:compaction 概况分析

本文档从框架的源码角度梳理了,hbase 在什么情况下会触发compaction,并通过官方文档说明出发minor 和major compaction的时间点。

52410
来自专栏玄魂工作室

看代码学渗透 Day5 - escapeshellarg与escapeshellcmd使用不当

--------------------------------------------------------------------------------...

34620

扫码关注云+社区

领取腾讯云代金券