I understand that the [L]
following a rewrite rule means that it's the "last" but I'm not clear what the scope is
For example, when there are multiple .htaccess files, some with [L]
, which ones will apply?
Example:
root.com/subdirectory1/subdirectory2
^ ^ ^
| | |
A B C
If there is an .htaccess file in each directory...
[L]
in an earlier file will other files get considered?You can have more than one . htaccess file on your hosting account, but each directory or folder can only have one. For example, you can have separate . htaccess files in your root folder and another in a sub-folder.
htaccess rewrite rules can be used to direct requests for one subdirectory to a different location, such as an alternative subdirectory or even the domain root. In this example, requests to http://mydomain.com/folder1/ will be automatically redirected to http://mydomain.com/folder2/.
There are two main directive of this module: RewriteCond & RewriteRule . RewriteRule is used to rewrite the url as the name signifies if all the conditions defined in RewriteCond are matching. One or more RewriteCond can precede a RewriteRule directive.
In your rewrite, the ^ signifies the start of the string, the (. *) says to match anything, and the $ signifies the end of the string. So, basically, it's saying grab everything from the start to the end of the string and assign that value to $1.
The [L]
flag does indeed mean "last", but it only applies to the rules in the current scope. From your question, it only applies to the rules in the htaccess file. If there are rules in a subdirectory, any rules in the parent directory are thrown out the window. They are not applied at all, unless you use the RewriteOptions Inherit
directive (which will append to the end of the rules any rules from the parent htaccess file).
Given your example:
root.com/subdirectory1/subdirectory2
^ ^ ^
| | |
A B C
If there are htaccess files in A, B, and C, and rewrite rules in all 3, if one requests http://root.com
(your "root.com" directory), only the rules in A get applied. If someone requests http://root.com/subdirectory1
, then only the rules in B get applied, and any rules in A are ignored (without the Inherit
option). Likewise, if someone goes to http://root.com/subdirectory1/subdirectory2
, then only the rules in C get applied, if there are no inheriting options.
The [L]
flag has no play in any of this, as the scope here is only within the rules of an htaccess file. Also note that [L]
doesn't necessarily mean "stop rewriting HERE", as the rewrite engine will loop until the URI going into the engine stops changing. The [L]
just means to stop rewriting in the current iteration of the rewrite engine's looping.
During the URL processing pipeline, apache attempts to map a URL to a file or resource. Lots of different modules have a part to play in the processing pipeline, like mod_rewrite or mod_proxy or mod_alias. At any point, this URI can change, be flagged to be redirected, be flagged to be proxied, be flagged to throw an error, etc. When the URI gets to mod_rewrite, the rewrite engine collects a bunch of rules from the vhost config and the appropriate htaccess file; note, 2 different scopes here. Each scope of rules are applied to the URI and if none of the rules match, then mod_rewrite is done. If one of the rules match, there is an internal redirect, meaning the URI is changed and then redirected back to the processing pipeline and mod_rewrite. Thus the same scope of rules get applied again, and if one of the rules match and gets applied, the same thing happens again the the rules loop again. There's a directive that you can set in the vhost/server config called LimitInternalRecursion
which sets the limit of these internal redirects. If the number of times the rewrite engine loops (i.e. redirects back to itself) exceeds this limit (which by default is 10, I think), then you get a 500 Internal Server error.
This might sound kind of weird but there's a lot of instances for wanting to do this. Example: remove all _
from URI and replace with -
:
RewriteRule ^(.*)_(.*)$ /$1-$2 [L]
If the URI is /a_b_c_d_foo
then the first time, the URI gets changed to /a_b_c_d-foo
, then it loops and gets changed to `/a_b_c-d-foo
, then again /a_b-c-d-foo
and by the 5th time around you get /a-b-c-d-foo
. It'll loop once more but since the ^(.*)_(.*)$
pattern doesn't match, the URI passes through the rewrite engine and the looping stops.
The problem arises when people create rules that don't take into account the looping, for example: rewrite /<anything>
to /foo/<anything>
:
RewriteRule ^(.*)$ /foo/$1 [L]
If the URI is /bar
then the first time the URI gets rewritten to /foo/bar
, and this is the desired result. But the URI gets internally redirected back into the rewrite engine and the same rule is matched again, resulting in: /foo/foo/bar
, then again: /foo/foo/foo/bar
, and again: /foo/foo/foo/foo/bar
, until the internal recursion limit is reached and you get a 500 Server Error.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With