Reputation: 11963
I thought the "L" flag was supposed to prevent subsequent rules from being applied. Yet in this example:
RewriteRule foo bar [L]
RewriteRule bar qux
I get http://mysite/foo
rewritten as http://mysite/qux
. I expected http://mysite/bar
. What am I missing here?
Upvotes: 2
Views: 151
Reputation: 25547
From the mod_rewrite
introduction:
Be sure to configure mod_rewrite's log level to one of the trace levels using the LogLevel directive. It is indispensable in debugging problems with mod_rewrite configuration, since it will tell you exactly how each rule is processed.
To turn on mod_rewrite tracing in Apache 2.4 you can use
LogLevel info mod_rewrite:trace3
However, in Apache 2.2 (which is the current favorite for production) you have to use
RewriteLog "/var/log/apache2/rewrite.log"
RewriteLogLevel 3
Your config
RewriteRule foo bar [L]
RewriteRule bar qux
is problematic because it must be run only once to work the way you want. The L flag in 2.2 doesn't do what you think when the rule is used in a Directory or .htaccess context.
If you are using RewriteRule in either .htaccess files or in sections, it is important to have some understanding of how the rules are processed. The simplified form of this is that once the rules have been processed, the rewritten request is handed back to the URL parsing engine to do what it may with it. It is possible that as the rewritten request is handled, the .htaccess file or section may be encountered again, and thus the ruleset may be run again from the start. Most commonly this will happen if one of the rules causes a redirect - either internal or external - causing the request process to start over.
However, moving those rules to a service config or virtual host context does work. (Verified on my local server.)
In general you want to write rules so it doesn't matter how many times they are processed. If you absolutely cannot do that, I still don't like using RewriteCond %{THE_REQUEST} ^GET\ /bar
to avoid looping or even RewriteCond %{THE_REQUEST} ^[^ ]+ /bar
but I don't have a have suggestion at the moment for Apache 2.2.
Apache 2.4 has the END flag for just this situation.
Upvotes: 1
Reputation: 811
The behavior of the [L]
flag is quite poorly described in the Apache docs. In order to understand how it works we first need to know how Apache handles RewriteRules. Let's take a simple example
RewriteRule ^something /somethingelse #1
RewriteRule ^somewhere /somewhereelse #2
RewriteRule ^someplace /anotherplace #3
In this situation with multiple rules and no [L]
flags, if we were to request /something
, Apache will rewrite that /somethingelse
(as per #1), then try rules #2 and #3. After all rules are processed it checks if the URL that came out of the RewriteRules is the same as the URL that went in. If it's not, Apache starts processing all the rules again, until input===output (or the maximum number of redirects is met, to prevent infinite loops).
Now, if we change rule #1 and add [L]
to it, and we request /something
again, Apache will rewrite it to /somethingelse
(as per #1), and then stop processing the rules, i.e., it will not process #2 and #3. But then, since the URL that came out is not the same as the URL that went in (that's the crux here), the processing restarts, and rules #2 and #3 will be processed anyway (and #1 also, but doesn't do anything anymore).
In your example if you want to prevent /bar
from being redirected to /qux
when it was rewritten by the first RewriteRule, you can use
RewriteRule foo bar [L]
RewriteCond %{THE_REQUEST} ^GET\ /bar
RewriteRule bar qux
That will rewrite /bar
to /qux
only if the user specifically requested /bar
, and not if the URL was rewritten from /foo
to /bar
first.
The trick here is that %{THE_REQUEST}
contains the exact HTTP header that was used for the request, and doesn't change when you rewrite the URL, so using that variable you can always check what the original request was for (in contrast to %{REQUEST_URI}
, which does change on every rewrite).
Upvotes: 1