There seem to be a decent number of mod_rewrite
threads floating around lately with a bit of confusion over how certain aspects of it work. As a result I've compiled a few notes on common functionality, and perhaps a few annoying nuances.
What other features / common issues have you run across using mod_rewrite
?
Equation can be done with following example:
Dynamic Load Balancing:
If you use the mod_proxy to balance your system, it's possible to add a dynamic range of worker server.
Another great feature are rewrite-map-expansions. They're especially useful if you have a massive amout of hosts / rewrites to handle:
They are like a key-value-replacement:
Then you can use a mapping in your rules like:
More information on this topic can be found here:
http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html#mapfunc
A better understanding of the [L] flag is in order. The [L] flag is last, you just have to understand what will cause your request to be routed through the URL parsing engine again. From the docs (http://httpd.apache.org/docs/2.2/rewrite/flags.html#flag_l) (emphasis mine):
So the [L] flag does stop processing any further rewrite rules for that pass through the rule set. However, if your rule marked with [L] modified the request, and you're in the .htaccess context or the
<Directory>
section, then your modifed request is going to be passed back through the URL parsing engine again. And on the next pass, it may match a different rule this time. If you don't understand what happened, it looks like your first rewrite rule with the [L] flag had no effect.The best way around this is to use the [END] flag (http://httpd.apache.org/docs/current/rewrite/flags.html#flag_end) instead of the [L] flag, if you truly want to stop all further processing of rules (and subsequent reparsing). However, the [END] flag is only available for Apache v2.3.9+, so if you have v2.2 or lower, you're stuck with just the [L] flag. In this case, you must rely on RewriteCond statements to prevent matching of rules on subsequent passes of the URL parsing engine. Or you must ensure that your RewriteRule's are in a context (i.e. httpd.conf) that will not cause your request to be re-parsed.
Where to place mod_rewrite rules
mod_rewrite
rules may be placed within thehttpd.conf
file, or within the.htaccess
file. if you have access tohttpd.conf
, placing rules here will offer a performance benefit (as the rules are processed once, as opposed to each time the.htaccess
file is called).Logging mod_rewrite requests
Logging may be enabled from within the
httpd.conf
file (including<Virtual Host>
):Common use cases
To funnel all requests to a single point:
Since Apache 2.2.16 you can also use
FallbackResource
.Handling 301/302 redirects:
Note: external redirects are implicitly 302 redirects:
Forcing SSL
Common flags:
[R]
or[redirect]
- force a redirect (defaults to a 302 temporary redirect)[R=301]
or[redirect=301]
- force a 301 permanent redirect[L]
or[last]
- stop rewriting process (see note below in common pitfalls)[NC]
or[nocase]
- specify that matching should be case insensitiveUsing the long-form of flags is often more readable and will help others who come to read your code later.
You can separate multiple flags with a comma:
Common pitfalls
Mixing
mod_alias
style redirects withmod_rewrite
Note: you can mix
mod_alias
withmod_rewrite
, but it involves more work than just handling basic redirects as above.Context affects syntax
Within
.htaccess
files, a leading slash is not used in the RewriteRule pattern:[L] is not last! (sometimes)
The
[L]
flag stops processing any further rewrite rules for that pass through the rule set. However, if the URL was modified in that pass and you're in the.htaccess
context or the<Directory>
section, then your modified request is going to be passed back through the URL parsing engine again. And on the next pass, it may match a different rule this time. If you don't understand this, it often looks like your[L]
flag had no effect.Our rewrite log shows that the rules are run twice and the URL is updated twice:
The best way around this is to use the
[END]
flag (see Apache docs) instead of the[L]
flag, if you truly want to stop all further processing of rules (and subsequent passes). However, the[END]
flag is only available for Apache v2.3.9+, so if you have v2.2 or lower, you're stuck with just the[L]
flag.For earlier versions, you must rely on
RewriteCond
statements to prevent matching of rules on subsequent passes of the URL parsing engine.Or you must ensure that your RewriteRule's are in a context (i.e.
httpd.conf
) that will not cause your request to be re-parsed.The deal with RewriteBase:
You almost always need to set RewriteBase. If you don't, apache guesses that your base is the physical disk path to your directory. So start with this:
mod_rewrite can modify aspects of request handling without altering the URL, e.g. setting environment variables, setting cookies, etc. This is incredibly useful.
Conditionally set an environment variable:
Return a 503 response:
RewriteRule
's[R]
flag can take a non-3xx value and return a non-redirecting response, e.g. for managed downtime/maintenance:will return a 503 response (not a redirect per se).
Also, mod_rewrite can act like a super-powered interface to mod_proxy, so you can do this instead of writing
ProxyPass
directives:Opinion: Using
RewriteRule
s andRewriteCond
s to route requests to different applications or load balancers based on virtually any conceivable aspect of the request is just immensely powerful. Controlling requests on their way to the backend, and being able to modify the responses on their way back out, makes mod_rewrite the ideal place to centralize all routing-related config.Take the time to learn it, it's well worth it! :)