RewriteRule中的Last[L]标志无效?

35
php_flag display_errors 1
php_value auto_prepend_file init.php
RewriteEngine on 
RewriteRule ^$  /id/authenticate [R]
RewriteRule ^login_openid$  /id/login_openid.php [QSA,L]
RewriteRule ^authenticate$  /id/authenticate.php [QSA,L]
RewriteRule ^facebook$  /id/facebook.php [QSA,L]
RewriteRule ^createfromopenid$  /id/createfromopenid.php [QSA,L]

RewriteRule .* - [L,R=403]
这是我的 .htaccess 文件。在服务器配置中,我只有 AllowOverride all
如果我请求 URL http://mydomain.com/id/authenticate,我会得到 403 错误。如果我删除最后一条规则,它就能工作了。难道 [L] 标志不应该阻止其他规则的执行吗?
编辑:
我的 htaccess 文件在子文件夹 "id" 中,所以规则有效。

1
你说除了最后一条规则,没有任何规则匹配是一个有效的想法,但是 “如果我删除最后一条规则,它就有效了。” 意味着它们确实有效。此外,你假设我的 htaccess 文件位于 Web 根目录而不是子文件夹中,这不是问题的上下文。然而,我不需要你的帮助,因为你又错了。我在 serverfault 上找到了正确的答案:http://serverfault.com/questions/241907/rewriterules-not-stopping-with-last-flag 结果发现 L 规则并不起作用,虽然 rewriterules 匹配。 - The Surrican
11
您可能正在寻找 END 标志(在 2.3.9 及更高版本可用)。 - hakre
2个回答

88

[L]规则可以正常工作--你只是不知道它具体是如何工作的。

当Apache看到[L]标志并匹配到规则(进行重写)时,Apache会进入下一次迭代并会再次从头开始匹配所有规则。 [L]标志的含义是“本次迭代中不要处理任何低于此规则的规则。”。

是的,Apache文档没有100%的清晰度(这意味着可以改进),但提供了足够的信息最终解决问题。


Apache将在以下几种情况下停止重写循环:

  1. 根本没有匹配任何规则(未进行重写);

  2. 匹配了“现在退出”的规则(例如RewriteRule .* - [L]);

  3. 发生了重写,但输入URL和最终URL相同(当“糟糕”的编写规则将同一URL重写为同一URL时,在第2-3次迭代时会发生这种情况。

    例如RewriteRule (.*) /index.php?page=$1 [L]

    • /hello => /index.php?page=hello
    • 在下一次迭代中,它将重写/index.php => /index.php?page=index.php
    • 然后在第三次迭代中,它将是/index.php => /index.php?page=index.php..这现在没有意义)。
  4. 达到了重写迭代限制(默认为10)--如果您进入了无限的重写循环,则该值由LimitInternalRecursion Directive控制。


根据以上所有信息,我可以说你当前的规则确实按预期工作。这意味着你需要改变逻辑并且摆脱最后一条规则(也许在父级 .htaccess 中处理此时刻,或者以不同的方式处理——这完全取决于你的应用程序是如何构建的,我不想要瞎猜)。


3
“-”作为目标URL意味着不进行重写,这与#3(迭代开始时URL等于迭代结束时的URL)几乎相同。由于URL没有被完全改变,因此Apache会退出循环。 - LazyOne
3
根据你的应用程序/重写逻辑,你可以使用这个规则:# do not do anything for already existing files RewriteCond %{REQUEST_FILENAME} -f [OR] RewriteCond %{REQUEST_FILENAME} -d RewriteRule .+ - [L] -- 这个规则运行良好。请勿修改已存在文件的任何操作。 - LazyOne
2
@Joe 让我们以你的规则为基础举个例子:请求URL是/id/login_openid初始迭代: URL被剥离到目录级别,等于login_openid。这将匹配规则#2(第5行)。重写发生并且新的URL是/id/login_openid.php,然后进行下一次迭代。 迭代#2: URL被剥离并且等于login_openid.php。这将不符合任何规则,除了最后一个规则,它告诉Apache中止请求(发送403响应) - 这就是你当前规则的工作方式。 - LazyOne
4
@LazyOne,嗯,那么Hakre上面提到的END标记怎么办? - Pacerier
3
@Pacerier 是的,它应该能够胜任工作(从描述来看,正是所需的),但它仅在2.3.x版本中可用——我个人尚未处理过它(所有服务器仍然是v2.2甚至更低版本),并不知道它有多广泛地使用。如果您控制整个服务器(可以安装该版本..或已经安装),那么您绝对应该尝试它。 - LazyOne
显示剩余5条评论

14

将此放置在您的cath all 规则前面。

RewriteCond %{ENV:REDIRECT_STATUS} !=200
问题在于一旦处理了[L]标记,所有下一个RewriteRules实际上都被忽略了,但是文件会从头开始再次被处理,现在使用新的URL。 如果文件已经被重定向,这个神奇的条件将不会处理"catch all"。 PS: 如果它不起作用,您可能需要微调一下条件: 200,!=200,^。 ,^$。 显然,对于重定向,变量被设置为200,但其他页面(错误和其他东西)也将其设置为某个值。现在这意味着您需要根据需要检查它是否为空,是否非空,是否为200或是否不为200。

1
我曾经遇到过非常类似的问题,而这个方法解决了我的问题。在我的 .htaccess 文件末尾使用以下代码作为“捕获所有”:RewriteCond %{ENV:REDIRECT_STATUS} !=200 RewriteRule .* /index.php [L] - Waboodoo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接