May 05

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. 我为什么要放弃订阅转而做牛人博客聚合
  3. Web设计与开发服务
  4. 调查: 你认为IT牛人博客聚合网站应该提供RSS吗?

Written by ideawu at 2010-05-05 10:05:09 | Views: 6050 | tags: ,

Leave a Reply

必须登录, 或者浏览器开启JavaScript支持才可以评论!


网站空间,免费试用