2010-05-05

链接包含”%2F”导致mod_rewrite失效

Views: 26403 | 1 Comment

IT牛人博客聚合网站(www.udpwork.com)用到了 Apache 的 mod_rewrite 模块进行 URL 重写. 但是, 在使用过程中曾经出现过一个比较诡异的问题. 开始认为是重写规则设置得不对, 后来才发现, 是"%2F"导致 Apache 直接返回 404 错误.

比如浏览查看某个标签下的文章列表的链接为


http://www.udpwork.com/tag/Linux

在重写之前的链接是


http://www.udpwork.com/?tag=Linux

PHP 脚本中用如下代码重写 URL

$url = '/tag/' . urlencode($tag);

相应的 Apache mod_rewrite 配置为

RewriteCond  %{REQUEST_URI}  ^/tag/.*$
RewriteRule  ^/tag/(.*)$  ?tag=$1  [L]

不过, 当 tag 中包含斜杠'/'时, 出现了问题, 服务器提示"404 - Not Found". 比如 tag 是 Unix/Linux, 生成的链接是


http://www.udpwork.com/tag/Unix%2FLinux

斜杠'/'被转义成'%2F', 那么最终的还原后的链接应该是


http://www.udpwork.com/?tag=Unix%2FLinux

直接访问这个未重写过的 URL, 是完全正常的. 但 Apache 一直报 404 错误. 后来才发现, 原来 Apache 有一个配置项"AllowEncodedSlashes", 默认是"Off", 也就是不允许请求路径(上例是 /tag/Unix%2FLinux)中包含编码后的斜杠'/'(在某些平台是反斜杠'\'). 这个选项的的相应代码在 mod_rewrite 模块被执行之前

// request.c
AP_DECLARE(int) ap_process_request_internal(request_rec *r){
	if (d->allow_encoded_slashes) {
		access_status = ap_unescape_url_keep2f(r->parsed_uri.path);
	} else {
		access_status = ap_unescape_url(r->parsed_uri.path);
	}
}

// util.c
AP_DECLARE(int) ap_unescape_url(char *url){
	if (IS_SLASH(*x) || *x == '\0')
		badpath = 1;
	...
	else if (badpath)
		return HTTP_NOT_FOUND;
}

默认不允许包含"%2F". 如果请求路径中包含了, 那么 ap_unescape_url() 函数认为是无效的路径, 直接返回 HTTP_NOT_FOUND, 最终浏览器得到的是"404 - Not Found"出错页面. 当然可以通过修改 Apache 配置来解决这个问题, 不过, 在 PHP 脚本中解决更通用

$url = '/tag/' . urlencode(str_replace('/', '%2F', $tag));

这样, 斜杠被两次转义, 变为"/tag/Unix%252FLinux". Apache 接收到请求后, 进行一次解码, 得到"/tag/Unix%2FLinux", 以参数"tag=Unix%2FLinux"交给 PHP 脚本处理, PHP 自己再将请求参数进行一次解码, $_GET['tag'] 的值就是"Unix/Linux"了.

Related posts:

  1. Apache用mod_rewrite配置子域名
  2. Web设计与开发服务
  3. 我为什么要放弃订阅转而做牛人博客聚合
  4. 最简单的PHP缓存方案 – Zend_Cache
Posted by ideawu at 2010-05-05 10:05:09 Tags: ,

One Response to "链接包含”%2F”导致mod_rewrite失效"

  • 非常感谢你的文章,对我帮助很大,我也进一步查了资料,发现apache 配置有1个参数专门解决了这个问题AllowEncodedSlashes,这个参数默认为off,看需要设置成on 或者nodecode 就可以了。具体可以看我blog的http://www.fyhome.com.cn/?p=139 Reply

Leave a Comment