【问题标题】:Redirect loop using Apache mod_rewrite (clean URLs)使用 Apache mod_rewrite 重定向循环(干净的 URL)
【发布时间】:2016-02-25 01:37:39
【问题描述】:

我的情况和this question中的情况很相似(其实代码很相似)。我一直在尝试创建一个 .htaccess 文件来使用没有文件扩展名的 URL,例如https://example.com/file 在适当的目录中找到 file.html,而且 https://example.com/file.html 重定向(使用 HTTP 重定向)到 https://example.com/file,因此只有一个规范 URL。用下面.htaccess:

Options +MultiViews
RewriteEngine On

# Redirect <...>.php, <...>.html to <...> (without file extension)
RewriteRule ^(.+)\.(php|html)$ /$1 [L,R]

就像上面提到的问题一样,我遇到了重定向循环。 (在我的例子中,找到对应的文件是通过MultiViews而不是单独的RewriteRule来实现的。)

但是,a solution adopted from this answer:

Options +MultiViews
RewriteEngine On

# Redirect <...>.php, <...>.html to <...> (without file extension)
RewriteCond %{THE_REQUEST} ^[A-Z]{3,}\s(.+)\.(php|html)
RewriteRule ^ %1 [L,R]

没有重定向循环。我很想知道差异来自哪里。两个文件在功能上不是等效的吗?为什么使用“普通”RewriteRule 会创建循环,而使用 %{THE_REQUEST} 不会?

请注意,我不是在寻找一种获取干净 URL 的方法(我可以使用我的文件的第二个版本或上​​面链接的问题的答案,它看起来像 %{ENV:REDIRECT_STATUS} ),但出于原因为什么这两种方法有效/无效,所以这与上面链接的问题不同。

注意:我只使用 mod_rewrite(没有 MultiViews)看到了同样的问题,所以这似乎不是由于 MultiViews 和 mod_rewrite 的执行顺序:

Options -MultiViews
RewriteEngine On

## Redirect <...>.php, <...>.html to <...> (without file extension)
# This works...
RewriteCond %{THE_REQUEST} ^[A-Z]{3,}\s(.+)\.(php|html)
RewriteRule ^ %1 [L,R]
# But this doesn’t!
#RewriteRule ^(.+)\.(php|html)$ /$1 [L,R]

# Find file with file extension .php or .html on the filesystem for a URL
# without file extension
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^ %{REQUEST_FILENAME}.php [L]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME}\.html -f
RewriteRule ^ %{REQUEST_FILENAME}.html [L]

区别在哪里?我希望这两种方法都可以工作,因为对文件的内部重写位于带有[L] 标志的.htaccess 的最后,因此之后不应该进行任何处理或重定向,对吧?

【问题讨论】:

    标签: apache .htaccess mod-rewrite url-rewriting


    【解决方案1】:
    # But this doesn’t!
    #RewriteRule ^(.+)\.(php|html)$ /$1 [L,R]
    

    此注释规则不起作用并导致重写循环的原因是因为您的其他规则正在添加 .html 扩展并将 %{REQUEST_URI} 变量更改为 /file.html 从而导致此规则再次执行。从规则中取出.html 会导致其他规则再次触发。这一直持续到达到最大递归限制。

    您还需要了解mod_rewrite 在循环中运行,直到规则不匹配。由于两个规则都在不断触发,因此mod_rewrite 不断循环。

    基于THE_REQUEST 的规则之所以有效,是因为THE_REQUEST 变量在执行其他重写规则后不会被覆盖。

    【讨论】:

    • 但是 [L] 标志不应该停止 mod_rewrite 循环吗?为什么它仍然继续追求例如RewriteRule ^ %{REQUEST_FILENAME}.html [L]?
    • No L 标志不会停止循环。 L 标志只是结束当前规则并强制 mod_rewrite 再次运行循环。
    • “当前规则”是指“.htaccess 文件的当前通行证”吗?否则旗帜就有点没用了……
    • 是的,这是最新的。它结束当前使用的RewriteRule,然后充当mod_rewrite 循环的continue。另请注意,如果您改用END(Apache 2.4+ 可用),那么这肯定会终止mod_rewrite 循环。
    【解决方案2】:

    如果您查看RewriteRule directive's documentation,您会注意到以下内容:

    在第一个 RewriteRule 上,它与 (%-decoded) 匹配 请求的 URL 路径,或者,在每个目录上下文中(见下文), 相对于每个目录上下文的 URL 路径。后续模式 与最后匹配的RewriteRule 的输出相匹配。

    因为,它会在每个目录的基础上进行匹配,一旦你输入以下内容:

    RewriteRule ^(.+)\.(php|html)$ /$1 [L,R]
    

    REQUEST_URI 变量发生变化,mod-rewrite 再次解析 URI。这会导致MultiViews 将 URL 重写为与此重定向 URL 匹配的正确文件并导致循环(每次重写时 URI 都会更改)。

    现在,当您将 THE_REQUEST 变量用于匹配时,URI 可能会在内部重写时发生变化,但服务器收到的实际请求永远不会改变,除非执行重定向。

    【讨论】:

    • 这里的问题与其他答案相同:[L] 标志不应该停止 mod_rewrite 循环(在我的最后一个示例中)吗?为什么它仍然继续追求例如RewriteRule ^ %{REQUEST_FILENAME}.html [L]?即使其他规则仍然匹配,它不应该停止在[L]吗?
    • @Socob L flag documentation 具有:“因此,如果您在其中一种情况下使用 RewriteRule 指令,那么您必须采取明确的步骤来避免规则循环,并且不要仅仅依靠[L]标志来终止一系列规则的执行,如下所示。".
    • 是的,我知道文档的那一部分。我想了解的是 为什么 [L] 在这种情况下对我没有帮助。是因为下面的部分吗? “有可能在处理重写的请求时,.htaccess 文件或&lt;Directory&gt; 部分可能会再次遇到,因此规则集可能会从头开始再次运行。最常见的情况是,如果其中一条规则导致重定向(内部或外部)导致请求过程重新开始。”
    • @Socob 啊,是的。我在之前的评论中粘贴文本时删除了错误的段落。
    • @Socob 一旦遇到L 标志,mod-rewrite 会执行任何要求的操作。现在,如果您应用了内部重定向,REQUEST_URI 会被更新,并且 .htaccess 会再次从头开始处理。如果您没有L 标志,mod-rewrite 将继续匹配规则直到文件结束,然后再次从头开始,如果有成功的REQUEST_URI 更改..
    猜你喜欢
    • 1970-01-01
    • 2013-07-04
    • 2012-02-29
    • 2016-02-22
    • 2023-03-18
    • 1970-01-01
    • 2021-05-21
    • 1970-01-01
    • 2010-12-06
    相关资源
    最近更新 更多