为什么这会导致无限的请求循环?

Lig*_*ica 5 .htaccess http-redirect

今天早些时候,我正在帮助一个.htaccess用例的人,并提出了一个有效的解决方案,但我自己无法弄明白!

他希望能够:

  • 浏览到 index.php?id=3&cat=5
  • 请参阅位置栏阅读 index/3/5/
  • 提供内容 index.php?id=3&cat=5

最后两个步骤相当典型(通常来自用户index/3/5首先进入),但第一步是必需的,因为他的网站中仍然有一些旧格式的链接,无论出于何种原因,都无法更改它们.所以他需要支持这两种 URL格式,让用户总是看到美化的格式.

经过多次考验后,我们想出了以下.htaccess文件:

RewriteEngine on

# Prevents browser looping, which does seem
#   to occur in some specific scenarios. Can't
#   explain the mechanics of this problem in
#   detail, but there we go.
RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule .* - [L]

# Hard-rewrite ("[R]") to "friendly" URL.
# Needs RewriteCond to match original querystring.
# Uses "?" in target to remove original querystring,
#   and "%n" backrefs to move its components.
# Target must be a full path as it's a hard-rewrite.
RewriteCond %{QUERY_STRING} ^id=(\d+)&cat=(\d+)$
RewriteRule ^index\.php$ http://example.com/index/%1/%2/? [L,R]

# Soft-rewrite from "friendly" URL to "real" URL.
# Transparent to browser.
RewriteRule ^index/(\d+)/(\d+)/$ /index.php?id=$1&cat=$2
Run Code Online (Sandbox Code Playgroud)

虽然它似乎是一个有点奇怪的用例(" 为什么不首先使用正确的链接? ",你可能会问),只是顺其自然.无论原始要求如何,这都是场景,这让我很生气.

没有第一条规则,客户端进入请求循环,尝试GET /index/X/Y/重复并获取302每次.检查REDIRECT_STATUS使一切顺利进行.但是我会想到,在最终规则之后,不再提供规则,客户不会再提出任何请求(注意,不[R]),一切都会变成肉汁.

那么......当我拿出第一条规则时,为什么这会导致请求循环?

Dav*_*d Z 5

由于无法修改您的设置,我不能肯定地说,但我相信这个问题是由于 mod_rewrite 的以下相对神秘的功能造成的:

当您在每个目录上下文中操作 URL/文件名时,mod_rewrite 首先将文件名重写回其相应的 URL(这通常是不可能的,但请参阅下面的 RewriteBase 指令以了解实现此目的的技巧),然后启动一个新的内部子请求使用新的 URL。这将重新启动 API 阶段的处理。

(来源:mod_rewrite 技术文档,我强烈建议阅读此内容)

换句话说,当您RewriteRule.htaccess文件中使用 a 时,新的重写的 URL 可能会映射到文件系统上完全不同的目录,在这种情况下,.htaccess原始目录中的文件将不再适用。RewriteRule因此,只要文件中的a.htaccess与请求匹配,Apache 就必须使用修改后的 URL从头开始​​处理。这意味着,除其他外,每一项RewriteRule都会被再次检查。

就您而言,发生的情况是您/index/X/Y/从浏览器访问。文件中的最后一条规则.htaccess将被触发,将其重写为/index.php?id=X&cat=Y,因此 Apache 必须使用 URL 创建一个新的内部子请求/index.php?id=X&cat=Y。这与您之前的外部重定向规则相匹配,因此 Apache 将 302 响应发送回浏览器以将其重定向到/index/X/Y/. 但请记住,浏览器从未见过该内部子请求;据它所知,它已经开始了/index/X/Y/。因此,您看起来好像被重定向到/index/X/Y/同一个 URL,从而触发了无限循环。

.htaccess除了性能影响之外,这可能是您应该尽可能避免将重写规则放入文件的更好原因之一。如果将这些规则移至主服务器配置,则不会出现此问题,因为规则上的匹配不会触发内部子请求。如果您无权访问主服务器配置文件,则可以绕过它的一种方法(编辑:或者我认为,尽管它似乎不起作用 - 请参阅评论)是添加[NS](无子请求)标志到您的外部重定向规则,

RewriteRule ^index\.php$ http://example.com/index/%1/%2/? [L,R,NS]
Run Code Online (Sandbox Code Playgroud)

一旦你这样做了,你就不再需要检查REDIRECT_STATUS.