Mod_Rewrite的L标志出现意外行为

9
我的Web应用程序结构如下:
/var/www/myapp/
    - www/
        - index.php
        - css.php
        - .htaccess

虚拟主机配置如下:
<VirtualHost *:80>
        ServerName www.example.org
        DocumentRoot /var/www/myapp/www
        DirectoryIndex index.php index.html
        <Directory /var/www/myapp/www>
                Options Indexes FollowSymLinks MultiViews
                AllowOverride All
                Order allow,deny
                allow from all
        </Directory>
</VirtualHost>

在 /var/www/myapp/www/.htaccess 文件中,有以下内容:
<IfModule mod_rewrite.c>
    RewriteEngine on
    RewriteBase /
    RewriteRule css css.php [L,NC]
    RewriteRule .* index.php
</IfModule>

现在,如果我调用www.example.org,我会正确地重定向到index.php,但是如果我调用www.example.org/css,我仍然总是被重定向到index.php。
如果我删除“RewriteRule .* index.php”这一行,然后调用www.example.org/css,我会正确地重定向到css.php。
问题出在哪里呢?
非常感谢所有人。
======== 编辑 =========
192.168.1.8 - - [14/Jul/2012:19:07:21 +0200] [www.example.org/sid#b747b6c0][rid#b7080058/initial] (3) [perdir /var/www/sviluppo/mattia_dev/example/www/] add path info postfix: /var/www/sviluppo/mattia_dev/example/www/DEV_2 -> /var/www/sviluppo/mattia_dev/example/www/DEV_2/css/example1/test.css
192.168.1.8 - - [14/Jul/2012:19:07:21 +0200] [www.example.org/sid#b747b6c0][rid#b7080058/initial] (3) [perdir /var/www/sviluppo/mattia_dev/example/www/] strip per-dir prefix: /var/www/sviluppo/mattia_dev/example/www/DEV_2/css/example1/test.css -> DEV_2/css/example1/test.css
192.168.1.8 - - [14/Jul/2012:19:07:21 +0200] [www.example.org/sid#b747b6c0][rid#b7080058/initial] (3) [perdir /var/www/sviluppo/mattia_dev/example/www/] applying pattern '^(DEV|TEST|PROD)\_[0-9]+\/(css|js|image|static)\/(.+)$' to uri 'DEV_2/css/example1/test.css'
192.168.1.8 - - [14/Jul/2012:19:07:21 +0200] [www.example.org/sid#b747b6c0][rid#b7080058/initial] (2) [perdir /var/www/sviluppo/mattia_dev/example/www/] rewrite 'DEV_2/css/example1/test.css' -> 'css/example1/test.css'
192.168.1.8 - - [14/Jul/2012:19:07:21 +0200] [www.example.org/sid#b747b6c0][rid#b7080058/initial] (3) [perdir /var/www/sviluppo/mattia_dev/example/www/] add per-dir prefix: css/example1/test.css -> /var/www/sviluppo/mattia_dev/example/www/css/example1/test.css
192.168.1.8 - - [14/Jul/2012:19:07:21 +0200] [www.example.org/sid#b747b6c0][rid#b7080058/initial] (2) [perdir /var/www/sviluppo/mattia_dev/example/www/] trying to replace prefix /var/www/sviluppo/mattia_dev/example/www/ with /
192.168.1.8 - - [14/Jul/2012:19:07:21 +0200] [www.example.org/sid#b747b6c0][rid#b7080058/initial] (5) strip matching prefix: /var/www/sviluppo/mattia_dev/example/www/css/example1/test.css -> css/example1/test.css
192.168.1.8 - - [14/Jul/2012:19:07:21 +0200] [www.example.org/sid#b747b6c0][rid#b7080058/initial] (4) add subst prefix: css/example1/test.css -> /css/example1/test.css
192.168.1.8 - - [14/Jul/2012:19:07:21 +0200] [www.example.org/sid#b747b6c0][rid#b7080058/initial] (1) [perdir /var/www/sviluppo/mattia_dev/example/www/] internal redirect with /css/example1/test.css [INTERNAL REDIRECT]
192.168.1.8 - - [14/Jul/2012:19:07:21 +0200] [www.example.org/sid#b747b6c0][rid#b63188e8/initial/redir#1] (3) [perdir /var/www/sviluppo/mattia_dev/example/www/] strip per-dir prefix: /var/www/sviluppo/mattia_dev/example/www/css/example1/test.css -> css/example1/test.css
192.168.1.8 - - [14/Jul/2012:19:07:21 +0200] [www.example.org/sid#b747b6c0][rid#b63188e8/initial/redir#1] (3) [perdir /var/www/sviluppo/mattia_dev/example/www/] applying pattern '^(DEV|TEST|PROD)\_[0-9]+\/(css|js|image|static)\/(.+)$' to uri 'css/example1/test.css'
192.168.1.8 - - [14/Jul/2012:19:07:21 +0200] [www.example.org/sid#b747b6c0][rid#b63188e8/initial/redir#1] (3) [perdir /var/www/sviluppo/mattia_dev/example/www/] strip per-dir prefix: /var/www/sviluppo/mattia_dev/example/www/css/example1/test.css -> css/example1/test.css
192.168.1.8 - - [14/Jul/2012:19:07:21 +0200] [www.example.org/sid#b747b6c0][rid#b63188e8/initial/redir#1] (3) [perdir /var/www/sviluppo/mattia_dev/example/www/] applying pattern '.*' to uri 'css/example1/test.css'
192.168.1.8 - - [14/Jul/2012:19:07:21 +0200] [www.example.org/sid#b747b6c0][rid#b63188e8/initial/redir#1] (2) [perdir /var/www/sviluppo/mattia_dev/example/www/] rewrite 'css/example1/test.css' -> 'index.php'
192.168.1.8 - - [14/Jul/2012:19:07:21 +0200] [www.example.org/sid#b747b6c0][rid#b63188e8/initial/redir#1] (3) [perdir /var/www/sviluppo/mattia_dev/example/www/] add per-dir prefix: index.php -> /var/www/sviluppo/mattia_dev/example/www/index.php
192.168.1.8 - - [14/Jul/2012:19:07:21 +0200] [www.example.org/sid#b747b6c0][rid#b63188e8/initial/redir#1] (2) [perdir /var/www/sviluppo/mattia_dev/example/www/] trying to replace prefix /var/www/sviluppo/mattia_dev/example/www/ with /
192.168.1.8 - - [14/Jul/2012:19:07:21 +0200] [www.example.org/sid#b747b6c0][rid#b63188e8/initial/redir#1] (5) strip matching prefix: /var/www/sviluppo/mattia_dev/example/www/index.php -> index.php
192.168.1.8 - - [14/Jul/2012:19:07:21 +0200] [www.example.org/sid#b747b6c0][rid#b63188e8/initial/redir#1] (4) add subst prefix: index.php -> /index.php
192.168.1.8 - - [14/Jul/2012:19:07:21 +0200] [www.example.org/sid#b747b6c0][rid#b63188e8/initial/redir#1] (1) [perdir /var/www/sviluppo/mattia_dev/example/www/] internal redirect with /index.php [INTERNAL REDIRECT]
192.168.1.8 - - [14/Jul/2012:19:07:21 +0200] [www.example.org/sid#b747b6c0][rid#b6310db8/initial/redir#2] (3) [perdir /var/www/sviluppo/mattia_dev/example/www/] strip per-dir prefix: /var/www/sviluppo/mattia_dev/example/www/index.php -> index.php
192.168.1.8 - - [14/Jul/2012:19:07:21 +0200] [www.example.org/sid#b747b6c0][rid#b6310db8/initial/redir#2] (3) [perdir /var/www/sviluppo/mattia_dev/example/www/] applying pattern '^(DEV|TEST|PROD)\_[0-9]+\/(css|js|image|static)\/(.+)$' to uri 'index.php'
192.168.1.8 - - [14/Jul/2012:19:07:21 +0200] [www.example.org/sid#b747b6c0][rid#b6310db8/initial/redir#2] (3) [perdir /var/www/sviluppo/mattia_dev/example/www/] strip per-dir prefix: /var/www/sviluppo/mattia_dev/example/www/index.php -> index.php
192.168.1.8 - - [14/Jul/2012:19:07:21 +0200] [www.example.org/sid#b747b6c0][rid#b6310db8/initial/redir#2] (3) [perdir /var/www/sviluppo/mattia_dev/example/www/] applying pattern '.*' to uri 'index.php'
192.168.1.8 - - [14/Jul/2012:19:07:21 +0200] [www.example.org/sid#b747b6c0][rid#b6310db8/initial/redir#2] (2) [perdir /var/www/sviluppo/mattia_dev/example/www/] rewrite 'index.php' -> 'index.php'
192.168.1.8 - - [14/Jul/2012:19:07:21 +0200] [www.example.org/sid#b747b6c0][rid#b6310db8/initial/redir#2] (3) [perdir /var/www/sviluppo/mattia_dev/example/www/] add per-dir prefix: index.php -> /var/www/sviluppo/mattia_dev/example/www/index.php
192.168.1.8 - - [14/Jul/2012:19:07:21 +0200] [www.example.org/sid#b747b6c0][rid#b6310db8/initial/redir#2] (1) [perdir /var/www/sviluppo/mattia_dev/example/www/] initial URL equal rewritten URL: /var/www/sviluppo/mattia_dev/example/www/index.php [IGNORING REWRITE]

看起来第一次重定向已经发生,但mod_rewrite没有停止导致内部重定向。然后,更改后的URL再次传递给mod_rewrite,第二次重定向发生,但现在匹配第二个规则。

我无法理解它,因为我已经使用[L]标志来确保mod_rewrite停止。

再次感谢


这很有趣。设置看起来没问题,我在我的系统上试过了,它可以工作。也许你想打开重写日志记录,以查看到底重写了什么:http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html#rewritelog - jCoder
我已经编辑了消息,你会找到一个日志。只关注内部重定向 :-) - MaCi
请阻止.htaccess文件内部重定向。 - MaCi
2个回答

43
你忽略了关于 L flag 的一个重要事实:
它很重要,如果你在这些[.htaccess, <Directory>] 上下文中使用RewriteRule指令, 你就必须采取明确的步骤来避免规则循环,并且不能仅仅依靠[L]标志来终止一系列规则的执行。
引自:L|last (Flag); bold by me 这意味着,仅仅使用L标记不能达到防止内部重定向的效果。因为你已经在.htaccess配置文件中指定了它,所以INTERNAL REDIRECT是必须发生的。L标记不是防止INTERNAL REDIRECT的正确标记。
让我们更仔细地看看你的问题和实际发生的情况:
我无法理解它,因为我只是想用[L]标记确保mod_rewrite停止。
这只是说明你对L标记的理解有误。它只会在当前重写中停止,这意味着在当前循环(内部循环)中不会处理下面的RewriteRule指令。
如果URI发生了改变,L将重新注入到下一轮循环(外层循环),如下所示的技术详细信息流程图http://httpd.apache.org/docs/current/rewrite/tech.html 为了突出显示L标记的使用和INTERNAL REDIRECT的发生位置,以下是相同的图形,其中包含你特定(第一个)URI重写的一些注释: enter image description here 它显示,L标记仅退出内部循环,但如果URI已被重写(更改)- 如在你的情况下 - 外层循环将确保所有重写规则再次传递已更改的URI。
相反,你可能想要像手册中该部分提供的以下示例一样制定条件:
RewriteBase /
RewriteCond %{REQUEST_URI} !=/index.php
RewriteRule ^(.*) /index.php?req=$1 [L,PT]

(PT 有自己的手册条目,它或多或少不是解决方案的一部分,只是注意因为我引用了原样的示例)

你实际想要使用的是 END 标志:

RewriteRule css css.php [END,NC]

如果你有所需的Apache版本(2.3.9及以上版本),请联系系统管理员。否则,你需要使用RewriteCond


谢谢您的反馈。用语言描述有点困难,所以我认为通过图形应该会更好。 - hakre
非常好的回答,我喜欢这些图表! - jmend
1
有史以来最好的mod_rewrite演示! - Reed
我完全同意,解释得非常好。 - tonix

3

hakre的回答很好地说明了正在发生的事情。除了使用END标志外,您还有几个选项:

  • Use %{ENV:REDIRECT_STATUS} to prevent any further rewrites. This will change after an internal redirect. See this page for more information what is actually happening:

    RewriteCond %{ENV:REDIRECT_STATUS} ^$
    RewriteRule css css.php [L,NC]
    
  • Use %{THE_REQUEST} to match against the request that was made to the server. Since this does not change when you internally redirect, this can be used to prevent further redirects

    RewriteCond %{THE_REQUEST} ^(GET|POST)\ /css\ HTTP
    RewriteRule ^ css.php [L,NC]
    
  • Use a dummy variable in the query string. This would allow to prevent redirecting by defining this variable in a link, but we will use it here to prevent multiple passes:

    RewriteCond %{QUERY_STRING} !noredir=1
    RewriteRule css css.php?noredir=1 [L,QSA,NC]
    
请注意,对于每个示例,您需要为每个单独的规则使用此结构。

另一种变体 - 但不适用于该变量 - 是将重写后的URL重写为自身,从而触发无更改退出L解析(例如Apache 2.2)。 - hakre

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