title: Why does this cause an infinite request loop?
body: <p>Earlier today, I was helping someone with an <code>.htaccess</code> use case, and <a href="https://stackoverflow.com/questions/5573485/php-htaccess-pretty-url-in-reverse/5573650#5573650">came up with a solution</a> that works but can't quite figure it out myself!</p>
  
  <p>He wanted to be able to:</p>
  
  <ul>
  <li>Browse to <code>index.php?id=3&amp;cat=5</code></li>
  <li>See the location bar read <code>index/3/5/</code></li>
  <li>Have the content served from <code>index.php?id=3&amp;cat=5</code></li>
  </ul>
  
  <p>The last two steps are fairly typical (usually from the user entering <code>index/3/5</code> 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 <em>both</em> URL formats, and have the user always end up seeing the prettified one.</p>
  
  <p>After much to-ing and fro-ing, we came up with the following <code>.htaccess</code> file:</p>
  
  <pre><code>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+)&amp;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&amp;cat=$2
  </code></pre>
  
  <p>Whilst it might seem to be a somewhat strange use case ("<em>why not just use the proper links in the first place?</em>", you might ask), just go with it. Regardless of the original requirement, this is the scenario and it's driving me mad.</p>
  
  <p>Without the first rule, the client enters into a request loop, trying to <code>GET /index/X/Y/</code> repeatedly and getting <code>302</code> each time. The check on <code>REDIRECT_STATUS</code> 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 <code>[R]</code>), and everything would be gravy.</p>
  
  <p>So... why would this result in a request loop when I take out the first rule?</p>