为什么这会导致无限请求循环? [英] Why does this cause an infinite request loop?

查看:28
本文介绍了为什么这会导致无限请求循环?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

今天早些时候,我正在帮助某人处理 .htaccess 用例和 想出了一个可行的解决方案,但我自己也搞不清楚!

Earlier today, I was helping someone with an .htaccess use case, and came up with a solution that works but can't quite figure it out myself!

他希望能够:

  • 浏览到index.php?id=3&cat=5
  • 查看地址栏读取index/3/5/
  • index.php?id=3&cat=5
  • 提供内容

最后两步相当典型(通常是用户首先输入index/3/5),但第一步是必需的,因为他仍然有一些旧格式的链接他的网站,无论出于何种原因,都无法更改它们.因此,他需要支持两种 URL 格式,并让用户始终看到经过美化处理的格式.

The last two steps are fairly typical (usually from the user entering index/3/5 in the first place), but the first step was required because he still had some old-format links in his site and, for whatever reason, couldn't change them. So he needed to support both URL formats, and have the user always end up seeing the prettified one.

经过一番折腾,我们想出了以下 .htaccess 文件:

After much to-ing and fro-ing, we came up with the following .htaccess file:

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

虽然这似乎是一个有点奇怪的用例(为什么不首先使用正确的链接?",你可能会问),就照做吧.不管最初的要求如何,这就是场景,它让我发疯.

Whilst it might seem to be a somewhat strange use case ("why not just use the proper links in the first place?", you might ask), just go with it. Regardless of the original requirement, this is the scenario and it's driving me mad.

没有第一条规则,客户端进入请求循环,反复尝试GET/index/X/Y/,每次都获取302.对 REDIRECT_STATUS 的检查使一切运行顺利.但我会认为在最终规则之后,将不再提供更多规则,客户端不会再提出任何请求(注意,没有 [R]),一切都会变得很糟糕.

Without the first rule, the client enters into a request loop, trying to GET /index/X/Y/ repeatedly and getting 302 each time. The check on REDIRECT_STATUS makes everything run smoothly. But I would have thought that after the final rule, no more rules would be served, the client wouldn't make any more requests (note, no [R]), and everything would be gravy.

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

So... why would this result in a request loop when I take out the first rule?

推荐答案

无法修改你的设置,我不能肯定,但我相信这个问题是由于 mod_rewrite 的以下相对神秘的特性:

Without being able to tinker with your setup, I can't say for sure, but I believe this problem is due to the following relatively arcane feature of mod_rewrite:

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

When you manipulate a URL/filename in per-directory context mod_rewrite first rewrites the filename back to its corresponding URL (which is usually impossible, but see the RewriteBase directive below for the trick to achieve this) and then initiates a new internal sub-request with the new URL. This restarts processing of the API phases.

(来源:mod_rewrite 技术文档,我高度评价 推荐阅读本文)

(source: mod_rewrite technical documentation, I highly recommend reading this)

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

In other words, when you use a RewriteRule in an .htaccess file, it's possible that the new, rewritten URL maps to an entirely different directory on the filesystem, in which case the .htaccess file in the original directory wouldn't apply anymore. So whenever a RewriteRule in an .htaccess file matches the request, Apache has to restart processing from scratch with the modified URL. This means, among other things, that every RewriteRule gets checked again.

就您而言,发生的情况是您从浏览器访问 /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,从而触发无限循环.

In your case, what happens is that you access /index/X/Y/ from the browser. The last rule in your .htaccess file triggers, rewriting that to /index.php?id=X&cat=Y, so Apache has to create a new internal subrequest with the URL /index.php?id=X&cat=Y. That matches your earlier external redirect rule, so Apache sends a 302 response back to the browser to redirect it to /index/X/Y/. But remember, the browser never saw that internal subrequest; as far as it knows, it was already on /index/X/Y/. So it looks to you as though you're being redirected from /index/X/Y/ to that same URL, triggering an infinite loop.

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

Besides the performance hit, this is probably one of the better reasons that you should avoid putting rewrite rules in .htaccess files when possible. If you move these rules to the main server configuration, you won't have this problem because matches on the rules won't trigger internal subrequests. If you don't have access to the main server configuration files, one way you can get around it (EDIT: or so I thought, although it doesn't seem to work - see comments) is by adding the [NS] (no subrequest) flag to your external redirect rule,

RewriteRule ^index.php$ http://example.com/index/%1/%2/? [L,R,NS]

一旦你这样做了,你应该不再需要检查REDIRECT_STATUS的第一条规则.

Once you do that, you should no longer need the first rule that checks the REDIRECT_STATUS.

这篇关于为什么这会导致无限请求循环?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆