3. Introduction to mod_rewrite¶
In the high and far-off times the Elephant, O Best Beloved, had no trunk.
—Rudyard Kipling, The Elephant’s Child
mod_rewrite is the power tool of Apache httpd URL mapping. Of course,
sometimes you just need a screwdriver, but when you need the power tool,
it’s good to know where to find it.
mod_rewrite provides sophisticated URL manipulation via regular expressions, and the
ability to do a variety of transformations, including, but not limited
to, modification of the request URL. You can additionally return a
variety of status codes, set cookies and environment variables, proxy
requests to another server, or send redirects to the client.
In this chapter we’ll cover mod_rewrite syntax and usage, and in the
next chapter we’ll give a variety of examples of using mod_rewrite in
common scenarios.
3.1. Where mod_rewrite fits in the request lifecycle¶
Before looking at configuration details, it helps to understand when
mod_rewrite runs during request processing. Apache httpd handles
requests in a series of phases — URL-to-filename translation,
authentication, authorization, fixup, content generation, and logging.
mod_rewrite hooks into two of these:
URL-to-filename translation — where server and
<VirtualHost>context rules run. This happens early, before authentication.Fixup — where per-directory context rules run (from
.htaccessfiles and<Directory>blocks). By this point the URL has already been mapped to a filesystem path, which is why per-directory rules see a stripped path rather than the full URL.
The following diagram shows the full pipeline and where
mod_rewrite’s two hooks sit:
This two-phase design explains many of the differences between
server-context and per-directory rules that the rest of this chapter
covers — particularly the path-stripping behavior in .htaccess
files and the re-entry loop that the [L] and [END] flags
control (see RewriteRule Flags).
3.1.1. Loading mod_rewrite¶
To use mod_rewrite in any context, you need to have the module loaded.
If you’re the server administrator, this means having the following line
somewhere in your Apache httpd configuration:
LoadModule rewrite_module modules/mod_rewrite.so
This tells httpd that it needs to load mod_rewrite at startup time, so
as to make its functionality available to your configuration files.
If you are not the server administrator, then you’ll need to ask your server administrator if the module is available, or experiment to see if it is. If you’re not sure, you can test to see whether it’s enabled in the following manner.
Create a subdirectory in your document directory. Let’s call it test_rewrite
Create a file in that directory called .htaccess and put the following text in it:
RewriteEngine on
Create another file in that directory called index.html containing the following text:
<html>
Hello, mod_rewrite
</html>
Now, point your browser at that location:
http://example.com/test_rewrite/index.html
You’ll see one of two things. Either you’ll see the words
Hello, mod_rewrite in your browser, or you’ll see the ominous words
Internal Server Error. In the former case, everything is fine -
mod_rewrite is loaded and your .htaccess file worked just fine. If you
got an Internal Server Error, that was httpd complaining that it didn’t
know what to do with the RewriteEngine directive, because mod_rewrite
wasn’t loaded.
If you have access to the server’s error log file, you’ll see the following in it:
Invalid command 'RewriteEngine', perhaps misspelled or defined by a module not included in the server configuration
Which is httpd’s way of saying that you used a directive
(RewriteEngine) without first loading the module that defines that
directive.
If you see the Internal Server Error message, or that log file message,
it’s time to contact your server administrator and ask if they’ll load
mod_rewrite for you.
However, this is fairly unlikely, since mod_rewrite is a fairly standard
part of any Apache HTTP Server’s bag of tricks.
3.1.2. RewriteEngine¶
In the section above, we used the RewriteEngine directive without
defining what it does.
The RewriteEngine directive enables or disables the runtime rewriting
engine. The directive defaults to off, so the result is that rewrite
directives will be ignored in any scope where you don’t have the
following:
RewriteEngine On
While we won’t always include that in every example in this book, it
should be assumed, from this point forward, that every use of
mod_rewrite occurs in a scope where RewriteEngine has been turned on.
3.2. How mod_rewrite interacts with other modules¶
A common source of confusion is mixing mod_rewrite rules with
mod_alias directives (Alias, Redirect, ScriptAlias,
RedirectMatch) or mod_proxy directives (ProxyPass) in
the same configuration scope. The results can be surprising if you
don’t know the processing order.
In the URL-to-filename translation phase:
mod_rewrite runs first. Your
RewriteRuledirectives are evaluated before anyAliasorRedirectdirectives.mod_alias runs second.
RedirectandRedirectMatchdirectives can still fire if the rewritten URL matches them — and in some contexts,Redirectcan preempt aRewriteRulevia early return.mod_proxy runs in a separate phase.
ProxyPassis not part of URL-to-filename translation. MixingRewriteRule [P]withProxyPassfor the same path can cause conflicts.
If you need a RewriteRule substitution to be passed through to
mod_alias for further processing (rather than being treated as
a final filesystem path), use the [PT] (passthrough) flag. Without
it, rewritten URLs bypass mod_alias entirely.
The following diagram illustrates the interaction:
3.2.1. mod_rewrite in .htaccess files¶
Before we go any further, it’s critical to note that things are different, in several important ways, if you have to use .htaccess files for configuration.
3.3. What are .htaccess files?¶
.htaccess files are per-directory configuration files, for use by people
who don’t have access to the main server configuration file. For the
most part, you put configuration directives into .htaccess files just as
you would in a <Directory> block in the server configuration, but
there are some differences.
The most important of these differences is that the .htaccess file is consulted every time a resource is requested from the directory in question, whereas configurations placed in the main server configuration file are loaded once, at server startup.
The positive side of this is that you can modify the contents of a .htaccess file and have the change take effect immediately, as of the next request received by the server.
The negative is that the .htaccess file needs to be loaded from the filesystem on every request, resulting in an incremental slowdown for every request. Additionally, because httpd doesn’t know ahead of time what directories contain .htaccess files, it has to look in each directory for them, all along the path to the requested resource, which results in a slowdown that grows with the depth of the directory tree.
In Apache httpd 2.2 and earlier, .htaccess files are enabled by default
- that is the configuration directive that enables them,
AllowOverride, has a default value of All. In 2.4 and later, it has
a default value of None, so .htaccess files are disabled by default.
A typical configuration to permit the use of .htaccess files looks like:
<Directory />
AllowOverride None
</Directory>
DocumentRoot /var/www/html
<Directory /var/www/html>
AllowOverride All
Options +FollowSymLinks
</Directory>
That is to say, .htaccess files are disallowed for the entire
filesystem, starting at the root, but then are permitted in the document
directories. This prevents httpd
from looking for .htaccess files in /, /var, and /var/www on the way to
looking in /var/www/html. [1]
Note that in order to enable the use of mod_rewrite directives in
.htaccess files, you also need to enable Options FollowSymLinks. A
RewriteRule may be thought of as a kind of symlink, because it allows
you to serve content from other directories via a rewrite. Thus, for
reasons of security, it is necessary to enable symlinks in order to use
mod_rewrite.
3.4. Ok, so, what’s the deal with mod_rewrite in .htaccess files?¶
There are two major differences that you must be aware of before we proceed any further. The exact implications of these differences will become more apparent as we go, but I wouldn’t want them to surprise you.
First, there are two directives that you cannot use in .htaccess files.
These directives are RewriteMap and (prior to httpd 2.4) RewriteLog.
These must be defined in the main server configuration. The reasons for
this will be discussed in greater length when we get to the sections
about those directives RewriteMap and RewriteLogging, respectively).
Second, and more importantly, the syntax of RewriteRule directives
changes in .htaccess context in a way that you’ll need to be aware of
every time you write a RewriteRule. Specifically, the directory path
that you’re in will be removed from the URL path before it is presented
to the RewriteRule.
The exact implications of this will become clearer as we show you examples. And, indeed, every example in this book will be presented in a form for the main config, and a form for .htaccess files, whenever there is a difference between the two forms. But we’ll start with a simple example to illustrate the idea.
Some of this, you’ll need to take on faith at the moment, since we’ve not yet introduced several of the concepts presented in this example, so please be patient for now.
Consider a situation where you want to apply a rewrite to content in the
/images/puppies/ subdirectory of your website. You have four options:
You can put the RewriteRule in the main server configuration file; You
can place it in a .htaccess file in the root of your website; You can
place it in a .htaccess file in the images directory; Or you can place
it in a .htaccess file in the images/puppies directory.
Here’s what the rule might look like in those various scenarios:
Location |
Rule |
|---|---|
Main config |
|
Root directory |
|
images directory |
|
images/puppies directory |
|
For the moment, don’t worry too much about what the individual rules do.
Look instead at the URL path that is being considered in each rule, and
notice that for each directory that a .htaccess file is placed in, the
directory path that RewriteRule may consider is relative to that
directory, and anything above that becomes invisible for the purpose of
mod_rewrite.
Don’t worry too much if this isn’t crystal clear at this point. It will become more clear as we proceed and you see more examples.
3.5. So, what do I do?¶
If you don’t have access to the main server configuration file, as it
the case for many of the readers of this book, don’t despair.
mod_rewrite is still a very powerful tool, and can be persuaded to do
almost anything that you need it to do. You just need to be aware of its
limitations, and adjust accordingly when presented with an example rule.
We aim to help you do that at each step along this journey.
3.5.1. RewriteOptions¶
The RewriteOptions directive controls several special behaviors of
the rewrite engine. You can specify multiple options separated by
spaces.
3.6. Inherit and InheritBefore¶
By default, rewrite rules are not inherited from parent contexts.
A <VirtualHost> does not inherit rules from the main server config;
a .htaccess file does not inherit rules from a parent directory’s
.htaccess.
RewriteOptions Inherit forces the current context to inherit the
parent’s rules, maps, and conditions. The inherited rules run after
the local rules.
RewriteOptions InheritBefore does the same, but the inherited
rules run before the local rules.
# In a .htaccess or <Directory> block:
RewriteOptions Inherit
3.7. InheritDown and InheritDownBefore¶
These are the inverse of Inherit: instead of a child saying “give
me my parent’s rules,” the parent says “push my rules into all
children.” This avoids needing RewriteOptions Inherit in every
child configuration.
InheritDown pushes the parent’s rules to run after each child’s
local rules. InheritDownBefore pushes them to run before.
2.4.8 Available in httpd 2.4.8 and later.
3.8. IgnoreInherit¶
If a parent has InheritDown set but a particular child should not
inherit, the child can use RewriteOptions IgnoreInherit to opt out.
2.4.8 Available in httpd 2.4.8 and later.
3.9. AllowNoSlash¶
By default, mod_rewrite ignores URLs that map to a directory on
disk but lack a trailing slash — it assumes mod_dir will handle the
redirect. If you’ve set DirectorySlash Off, enable
AllowNoSlash so that rewrite rules can match directory URLs without
a trailing slash.
2.4 Available in httpd 2.4.0 and later.
3.10. AllowAnyURI¶
In server/vhost context (since httpd 2.2.22), mod_rewrite only
processes requests whose URI is a valid URL-path. This is a security
measure (see CVE-2011-3368 and CVE-2011-4317). AllowAnyURI lifts
that restriction.
Warning
Enabling this makes the server vulnerable to security issues if rewrite rules are not carefully authored. Use with extreme caution.
2.4.3 Available in httpd 2.4.3 and later.
3.11. MergeBase¶
Copies the value of RewriteBase from where it’s explicitly defined
into any sub-directory or sub-location that doesn’t define its own.
This was the default behavior in httpd 2.4.0–2.4.3; the option restores
it.
2.4.4 Available in httpd 2.4.4 and later.
3.12. IgnoreContextInfo¶
When a relative substitution is made in per-directory context and
RewriteBase has not been set, mod_rewrite uses extended URL
and filesystem context information (provided by modules like
mod_userdir and mod_alias) to resolve the substitution back
into a URL. This option disables that behavior.
2.4.16 Available in httpd 2.4.16 and later.
3.13. LegacyPrefixDocRoot¶
Prior to 2.4.26, when a substitution was an absolute URL matching the current virtual host, the URL could be reduced to a local path and the document root would be prepended. This option restores that legacy behavior.
2.4.26 Available in httpd 2.4.26 and later.
3.14. LongURLOptimization¶
Reduces memory usage for long, unoptimized rule sets that repeatedly
expand long values in RewriteCond and RewriteRule variables.
trunk Available in httpd trunk (future 2.5.x) only — not yet in any stable release.
3.14.1. RewriteBase¶
The RewriteBase directive sets the base URL for per-directory
rewrites. It is only valid in per-directory context (.htaccess files
and <Directory> blocks) and is ignored in server or virtual host
context.
When mod_rewrite processes a rule in .htaccess, it strips the
local directory prefix from the URL before matching, then prepends it
back after substitution. RewriteBase overrides what gets prepended.
Consider a .htaccess file in /var/www/html/app/, where the
URL /app/ maps to that directory:
# /var/www/html/app/.htaccess
RewriteEngine On
RewriteBase /app/
RewriteRule ^page/(.*)$ index.php?page=$1 [L]
Without RewriteBase /app/, the substitution index.php?page=foo
would be interpreted relative to the filesystem path, not the URL path,
and the result might not be what you expect.
The most common value is simply:
RewriteBase /
This tells mod_rewrite that all substitutions should be treated as
relative to the document root.
When do you need RewriteBase?
In
.htaccessfiles when your rewrite substitutions are relative paths (not starting with/).When the URL path to the directory containing the
.htaccessdiffers from the filesystem path (e.g., due toAlias).You do not need it in
<VirtualHost>or server config — there,RewriteRuleoperates on the full URL-path and no prefix stripping occurs.
When can you omit it?
When all your substitutions use absolute URL-paths (starting with
/).When the URL-to-filesystem mapping is straightforward (
DocumentRoot+ URL-path = filesystem path).
A common source of confusion: people put RewriteBase in server
config or <VirtualHost> blocks where it has no effect, then wonder
why their rules behave unexpectedly. If you’re not in a .htaccess
or <Directory> context, you don’t need it.