我已经设置了.htaccess
将针对包含目录的请求重定向/htdocs/foo
到文件bar.php
。
首先:我知道通常如何实现这一点,但由于我无法控制的原因,还想.htaccess
检查 URL 是否解析为常规文件,如果不是,则重定向。
听起来不太复杂,但我无法解决这个问题。:[
这是我的.htaccess
:
RewriteRule ^$ http://www.example.com/foo/bar.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* doh.php
这些规则不会重定向www.example.com/foo
到bar.php
,而是doh.php
。
我检查了error.log
,由于某种原因,虽然文件bar.php
存在,但重写条件却匹配。
RewriteCond: input='http://www.example.com/foo/bar.php' pattern='!-f' => matched
bar.php
显然,如果重写的 URL 指向该文件,Apache 不会将其视为常规文件。www.example.com/foo/bar.php
按预期工作。
知道原因吗?
编辑#1:
为了澄清起见,这是文件结构:
/ (File system root)
|
+ htdocs (Document root)
|
+ foo
|
+ .htaccess
+ bar.php
+ doh.php
编辑#2:
我正在运行 Apache 2.4。
解决方案:
预计RewriteCond
包含REQUEST_FILENAME
一个本地路径。由于缺少L
标志,RewriteRule
设置REQUEST_FILENAME
为显然不是本地路径的 URL,因此测试!-f
匹配。
最终,附加L
标志会RewriteRule
产生所需的行为:
RewriteRule ^$ http://www.example.com/foo/bar.php [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* doh.php
URL 重写后,http://www.example.com/foo/bar.php
将启动一个新的流程循环,将重写的 URL 重新映射到与 的测试/htdocs/foo/bar.php
不匹配的本地路径。并让其通过。:)RewriteCond
!-f
/htdocs/foo/bar.php
答案1
RewriteCond: input='/htdocs/foo/bar.php' pattern='!-f' => matched
不确定这是否被过度举例了,但除非有“其他事情”发生,否则根据上述指令,这并不像预期的那样。
由于您的初始(隐式)重定向缺少该L
标志,因此处理将立即继续执行下一个指令。此时,我希望在日志中看到类似以下内容:
RewriteCond: input='http://www.example.com/foo/bar.php' pattern='!-f' => matched
第一个 之后RewriteRule
,该REQUEST_FILENAME
指令将立即更新为最后一个 的输出RewriteRule
。http://www.example.com/foo/bar.php
。在此阶段,它不会重新映射到文件系统。(除非您指定了相对路径代换,在这种情况下,目录前缀将被添加 - 这看起来就像您在日志中看到的一样 - 但这与问题中的指令不对应。)
因此,上述条件确实是“匹配”——“绝对 URL”不可能作为文件系统上的文件存在。
然后你会得到一个损坏302 “重写”至doh.php
。该请求是内部重写到doh.php
,但 HTTP 状态从之前的“重定向”设置为 302,但没有Location
标头,因此没有外部重定向。(虽然您似乎暗示有一个“重定向”到doh.php
?)
RewriteRule ^$ http://www.example.com/foo/bar.php
为了触发外部重定向(考虑到稍后的重写),您需要包含L
(last
)标志以停止当前一轮的处理。
您还应该明确地包含R
标志。这里暗示了 302 重定向,因为您已在 中明确包含了方案 + 主机名RewriteRule
。
例如:
RewriteRule ^$ http://www.example.com/foo/bar.php [R,L]
在旁边:
这些规则不会重定向
www.example.com/foo
请注意,如果您请求的/foo
,不带尾随斜杠,并且/foo
是一个物理目录,则 mod_dir 将首先隐式 301 重定向到,/foo/
以便通过附加尾随斜杠来“修复”URL。