今天在调试一个其他问题时碰巧发现了一个奇怪的情况:根据我的 RewriteRule ,明明应该被 rewrite 到静态 HTML 缓存的请求,却在返回的 HTTP 头里看到了 PHP !这个 rewrite 规则应该是经过测试的,怎么突然就失效了?
第一怀疑对象是自己手贱。毕竟是个人 VPS 上的 Apache 配置,也没有使用版本控制之类的,不能确定是不是改过什么。就排查了一下。结果发现 RewriteLogLevel
这个配置竟然无效了?一查才发现,原来 Apache 2.4 里把 rewrite 日志合并到了 error 中,只需要在错误日志里加上 rewrite:traceN
就行了(其中 N 是错误级别,4足够,8应该是最详细的)。盯着错误日志看了半天,发现一个“有趣”的现象——明明已经正确匹配、 rewrite 到了静态文件,却又接着发起了 sub request ,请求了 index.php ?!
为了更好说明问题,我把我的配置简化了一下,大概是这样:
RewriteEngine on RewriteBase / RewriteCond %{DOCUMENT_ROOT}/cache/$1/static.html -f RewriteRule ^(.*) "/cache/$1/static.html" [L]
简单来说就是判断一下请求的文件是不是在 cache 文件夹里有静态版本。这个简化的规则也许有一些 edge case 没有处理好,但不妨碍我们说明问题。
日志显示的情况是:
- 访问
http://mysite/
。 - 判断
/cache//static.html
(等价于/cache/static.html
)存在。 - rewrite 生效,转为请求
/cache//static.html
。 - 然而,接下来又开始了一系列 sub request ,请求
index.html
、index.php
、index.cgi
之类。很明显,这是 mod_dir 模块的DirectoryIndex
在生效。 - 根目录下本来就有 index.php ,于是被 serve 了……
请求就这样结束了,“静态缓存”完全没有也不可能起效。可是,这个配置我印象深刻,简化版则更是简化到了不能更简的地步, rewrite 显然就是这么用的。我不禁怀疑—— Apache 2.4 修改了这么多,带来了巨大的兼容性问题,莫非这也是其中之一?
果然,我找到了这两个 bug :
- https://issues.apache.org/bugzilla/show_bug.cgi?id=53794
- https://issues.apache.org/bugzilla/show_bug.cgi?id=53929
基本是同一个问题。简单来说就是 DirectoryIndex
现在总是会起效,不论 URL 是否已经被 mod_rewrite 在内的其他 handler 修改过。值得注意的是第二个 issue ,有补丁被接受了,目前(2015.1.15)在 trunk 里,应该会和 2.4.8 一起发布。果然,在 2.4.8 的 changelog 里看到了这个:
*) mod_dir: Add DirectoryCheckHandler to allow a 2.2-like behavior, skipping execution when a handler is already set. PR53929. [Eric Covener]
然后最新版的文档里新增了这个配置项: DirectoryCheckHandler 。坑爹的是,2.4之前的版本可以看作这个配置是 on ,而2.4开始默认变成了 off 。想要关掉?必须得 2.4.8 才行!现在还没正式发布呢!
Comments ( 2 )