Why does this cause an infinite request loop?

Earlier today, I helped someone with the precedent .htaccess , and came up with a solution that works, but cannot figure it out myself!

He wanted to be able to:

  • Go to index.php?id=3&cat=5
  • See location line index/3/5/ more details.
  • Prepare the contents with index.php?id=3&cat=5

The last two steps are pretty typical (usually from the user who enters index/3/5 in the first place), but the first step was necessary because he still had links to the old format on his site and, for some reason, Do not change them. Therefore, he needed to support both URL formats, and the user always ended up seeing the prefix.

After repeated input and ascent, we received 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 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 

Although this may seem like a somewhat weird use case (โ€œwhy not just use the right links first?โ€ You may ask), just go with it. Regardless of the initial requirement, this is a scenario, and it drives me crazy.

Without the first rule, the client enters the request loop, repeatedly repeating GET /index/X/Y/ and receiving 302 each time. Checking REDIRECT_STATUS does everything fine. But I would think that after the final rule, no rules will be respected anymore, the client will not make more requests (note that there is no [R] ), and everything will be poured.

So ... why does this lead to a request loop when I select the first rule?

+5
.htaccess
Apr 07 2018-11-11T00:
source share
2 answers

Unable to mess with my installation, I canโ€™t say for sure, but I believe that this problem is related to the following relatively mysterious feature of mod_rewrite:

When you manipulate the URL / filename in the context of each directory, mod_rewrite first overwrites the file name back to the corresponding URL (which is usually not possible, but see the RewriteBase directive below for the trick to achieve this), and then initiates a new internal sub-request with a new URL address. This resumes the processing of the API phases.

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

In other words, when you use the RewriteRule in the .htaccess file, it is possible that the new, rewritten URL maps to a completely different directory on the file system, in which case the .htaccess file in the original directory will no longer be used. Therefore, whenever the RewriteRule in the .htaccess file matches the request, Apache must restart processing from scratch with the URL changed. This means, among other things, that every RewriteRule is checked again.

In your case, what happens, you get access to /index/X/Y/ from the browser. The last rule in your .htaccess file fires triggers, rewriting them to /index.php?id=X&cat=Y , so Apache must create a new internal subquery with the URL /index.php?id=X&cat=Y . This matches your previous external redirect rule, so Apache sends the 302 response back to the browser to redirect it to /index/X/Y/ . But remember, the browser has never seen this internal subquery; as far as he knows, he was already at /index/X/Y/ . Therefore, it seems to you that you are redirecting from /index/X/Y/ to the same URL, causing an infinite loop.

Besides improving performance, this is probably one of the best reasons why you should avoid rewriting rules in .htaccess files whenever possible. If you move these rules to the configuration of the main server, you will not have this problem, because matches in the rules will not cause internal subqueries. If you do not have access to the configuration files of the main server, one of the ways you can get around it (EDIT: or so I thought, although it does not seem to work - see Comments) by adding [NS] (no subrequest) for your external redirection rule,

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

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

+3
Apr 07 2018-11-11T00:
source share

The solution below worked for me.

 RewriteEngine on RewriteBase / #rule1 #Guard condition: only if the original client request was for index.php RewriteCond %{THE_REQUEST} ^[AZ]{3,9}\ /index\.php [NC] RewriteCond %{QUERY_STRING} ^id=(\d+)&cat=(\d+)$ [NC] RewriteRule . /index/%1/%2/? [L,R] #rule 2 RewriteRule ^index/(\d+)/(\d+)/$ /index.php?id=$1&cat=$2 [L,NC] 

Thats what i think is going on

From the above steps

  • Go to index.php? id = 3 & cat = 5
  • See location line read index / 3/5 /
  • Content contained in index.php? id = 3 & cat = 5

In step 1, Rule 1 matches and redirects to the location bar and performs step 2.

In step 3, rule 2 now matches and is rewritten in index.php.

The rules are repeated for Davidโ€™s reasons, but since THE_REQUEST unchanged after setting the original request, it still contains /index/3/5 THE_REQUEST , so Rule 1 does not match.

Rule 2 does not match any, and the result of index.php is served.

Most other variables change, for example. REQUEST_URI Their modification during the processing of rules and the incorrect expectation of a pattern matching against the original request are a common cause of infinite loops.

She seems quite esoteric sometimes, but I'm sure there is a logical reason for its complexity :-)

EDIT

Of course there are two different requests

There are 2 client requests, the source from Step1 and one of the external redirects in step 2.

What I overshadowed above is that when Rule 2 matches the second request, it is rewritten in /index.php and causes an internal redirection. This forces you to reload the .htaccess file for the / directory again (it could easily be another different directory with different .htaccess rules) and run all the rules again.

So ... why does this lead to a request loop when I select the first rule?

When rules re-run, the first rule now unexpectedly matches the result of rewriting Rule2 and redirects, causing an infinite loop.

Davidโ€™s response contains most of this information, and I mean โ€œfor the reasons David gave.โ€

However, the main thing is that you need an additional condition, either your condition, which stops the further processing of the rules in internal redirects, or mine, which prevents the coordination of rule 1, is necessary to prevent an infinite loop.

0
Jan 08 2018-12-12T00:
source share



All Articles