如果请求子目录,Apache 重写会导致无限递归

如果请求子目录,Apache 重写会导致无限递归

我有一个重写规则,在大多数情况下都有效。但是,如果我请求一个带有不存在的子目录的页面,并且该子目录与现有文件匹配,则会导致无限递归。

    Options +FollowSymLinks
    AllowOverride None
    Require all granted

    RewriteEngine on

    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME}\.php -f
    RewriteRule ^(.*)$ $1.php [L]

如果我要求,这将有效:

  • test.php
  • test(它提供 test.php 服务)
  • badname.phpbadname(正确给出 404)
  • directory/file_that_exists或者directory/file_that_exists.php

一切正常。但如果test.php存在,并且我请求test/test,则会导致无限递归。错误日志如下所示:

[Wed May 23 08:16:28.176564 2018] [core:debug] [pid 27054] core.c(3620): [client 0.0.0.0:57764] AH00122: redirected from r->uri = /test/test.php.php.php
[Wed May 23 08:16:28.176566 2018] [core:debug] [pid 27054] core.c(3620): [client 0.0.0.0:57764] AH00122: redirected from r->uri = /test/test.php.php
[Wed May 23 08:16:28.176568 2018] [core:debug] [pid 27054] core.c(3620): [client 0.0.0.0:57764] AH00122: redirected from r->uri = /test/test.php
[Wed May 23 08:16:28.176570 2018] [core:debug] [pid 27054] core.c(3620): [client 0.0.0.0:57764] AH00122: redirected from r->uri = /test/test

我做错了什么?当它发现test/test.php不存在时,它不应该失败吗?(RewriteCond %{REQUEST_FILENAME}\.php -f)如果我只是附加一个斜线,我会得到相同的结果,例如test/

答案1

test/test.php当它发现不存在时,它不应该失败吗?( RewriteCond %{REQUEST_FILENAME}\.php -f)如果我只是添加一个斜线,我会得到相同的结果,例如test/

如果执行的是这样的检查,它将会失败,但事实并非如此。

服务器REQUEST_FILENAME变量包含文件系统路径URL 已映射到文件系统。这不一定与RewriteRule 图案對抗。

例如,在您的例子中,当请求/test/test.php没有名为 的物理子目录时/testREQUEST_FILENAME服务器变量包含以下形式的字符串/absolute/filesystem/path/to/test(即,请求的 URL 直到最后一个已知目录 + 第一个路径段/test)。并且由 匹配的 URL 路径RewriteRule 图案将是/test/test.php(您将反复附加它,.php因为/test.php确实存在,如前面的RewriteCond指令所检查的那样)。

您可以通过检查 URL 路径(构建一个绝对文件系统路径来检查)来解决这个问题,就像 @shanew 建议的那样。例如:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}/$1\.php -f
RewriteRule ^(.*)$ $1.php [L]

为了避免对每个请求进行所有这些文件系统检查,一种优化方法是首先检查扩展名.php(和.css.js等),然后再检查文件是否存在:

RewriteCond %{REQUEST_URI} !\.(php|css|js|jpg|png)$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}/$1\.php -f
RewriteRule ^(.*)$ $1.php [L]

当请求的 URL 已经以 、 等结尾时,这可以避免不必要的.php检查.css

答案2

如果你在目录部分或 .htaccess 文件中执行此操作,则应该能够通过在 RewriteRule 中添加 END 选项来停止无限递归,以便它看起来像这样

重写规则 ^(.*)$ $1.php [L,END]

这将停止重写引擎执行任何后续的重写处理(而 L 仅停止当前一轮的重写处理)。Apache 2.4 重写标志文档解释得更详细。

话虽如此,我不确定这是否能让你得到你真正想要的行为,因为它仍然会将 /test/test 更改为 /test/test.php,尽管它不存在。为了解决这个问题,我认为你需要测试整个文件路径,而不仅仅是文件的名称。为此,请更改:

%{请求文件名}

进入

%{DOCUMENT_ROOT}%{REQUEST_URI}

此外,为了安全起见,我会在顶部添加另一个 RewriteCond,这样如果文件名已经以 .php 结尾,规则就不会触发(将其放在第一位意味着如果它返回 true,则可以跳过其他条件):

%{REQUEST_FILENAME} "!\.php$"

相关内容