blob: 7ce37487f4718f16036d23e869fc894159ea4a14 [file]
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE manualpage SYSTEM "../style/manualpage.dtd">
<?xml-stylesheet type="text/xsl" href="../style/manual.en.xsl"?>
<!-- $LastChangedRevision$ -->
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manualpage metafile="tech.xml.meta">
<parentdocument href="./">Rewrite</parentdocument>
<title>Apache mod_rewrite Technical Details</title>
<summary>
<p>This document discusses some of the technical details of <module>mod_rewrite</module>
and URL matching.</p>
</summary>
<seealso><a href="../mod/mod_rewrite.html">Module documentation</a></seealso>
<seealso><a href="intro.html">mod_rewrite introduction</a></seealso>
<seealso><a href="remapping.html">Redirection and remapping</a></seealso>
<seealso><a href="flags.html">RewriteRule Flags</a></seealso>
<seealso><a href="vhosts.html">Virtual hosts</a></seealso>
<seealso><a href="rewritemap.html">Using RewriteMap</a></seealso>
<seealso><a href="htaccess.html">Per-directory Rewrites</a></seealso>
<seealso><a href="avoid.html">When not to use mod_rewrite</a></seealso>
<section id="InternalAPI"><title>API Phases</title>
<p>The Apache HTTP Server handles requests in several phases. At
each of these phases, one or more modules may be called upon to
handle that portion of the request lifecycle. Phases include things
like URL-to-filename translation, authentication, authorization,
content, and logging. (This is not an exhaustive list.)</p>
<p><module>mod_rewrite</module> acts in two of these phases (or "hooks", as they are
often called) to influence how URLs may be rewritten.</p>
<p>First, it uses the URL-to-filename translation hook, which occurs
after the HTTP request has been read, but before any authorization
starts. Secondly, it uses the Fixup hook, which is after the
authorization phases, and after per-directory configuration files
(<code>.htaccess</code> files) have been read, but before the
content handler is called.</p>
<p>After a request comes in and a corresponding server or
virtual host has been determined, the rewriting engine starts
processing any <module>mod_rewrite</module> directives appearing in the
per-server configuration. (i.e., in the main server configuration file
and <directive module="core" type="section">Virtualhost</directive>
sections.) This happens in the URL-to-filename phase.</p>
<p>A few steps later, once the final data directories have been found,
the per-directory configuration directives (<code>.htaccess</code>
files and <directive module="core"
type="section">Directory</directive> blocks) are applied. This
happens in the Fixup phase.</p>
<p>In each of these cases, <module>mod_rewrite</module> rewrites the
<code>REQUEST_URI</code> either to a new URL, or to a filename.</p>
<p>In <glossary ref="perdirectory">per-directory context</glossary>,
rules are applied during the Fixup phase after the URL has already
been translated to a filename. This changes what the pattern matches
against and how substitutions are handled. See the
<a href="htaccess.html#path-stripping">Per-directory Rewrites</a>
document for practical details on path stripping, RewriteBase, and
how to avoid looping.</p>
</section>
<section id="order"><title>Module Processing Order</title>
<p><module>mod_rewrite</module> and <module>mod_alias</module> both
operate during the URL-to-filename translation phase, but
<module>mod_rewrite</module> runs <strong>first</strong> regardless
of the order in which directives appear in the configuration file.
This is determined by the hook priority each module registers, not
by source order.</p>
<p>The practical consequence: when both <directive
module="mod_rewrite">RewriteRule</directive> and <directive
module="mod_alias">Redirect</directive> (or <directive
module="mod_alias">RedirectMatch</directive>) are present in the
same server or virtual-host context, the rewrite rules are
evaluated first. If a <code>RewriteRule</code> matches and rewrites
the URL-path (or returns a redirect), <code>Redirect</code> never sees
the request.</p>
<p class="figure">
<img src="../images/rewrite_module_order.png"
alt="Side-by-side comparison of module processing order:
in server context, mod_rewrite runs first in the
URL-to-filename phase then mod_alias runs second; in
per-directory context, mod_alias runs first in the
URL-to-filename phase, then mod_rewrite runs later in the
Fixup phase" /><br />
<dfn>Figure:</dfn> Module processing order reversal between server and per-directory context
</p>
<highlight language="config">
# In this configuration, the Redirect is never reached for /old
# because the RewriteRule matches first — even though
# the Redirect appears earlier in the file.
Redirect "/old" "http://example.com/new"
RewriteRule "^/old" "/other" [L]
</highlight>
<note><title>Per-directory context reverses the order</title>
<p>In <glossary ref="perdirectory">per-directory context</glossary>,
the situation is different. <module>mod_alias</module> directives like
<code>Redirect</code> still run in the URL-to-filename translation
phase, but <module>mod_rewrite</module> rules run later, in the
Fixup phase. This means that in per-directory context,
<code>Redirect</code> is evaluated <em>before</em>
<code>RewriteRule</code>.</p>
</note>
<p>Because of this inconsistency between contexts, mixing
<module>mod_rewrite</module> and <module>mod_alias</module>
directives in the same scope is a common source of confusion. The
simplest advice: choose one module for a given task. If you need
rewrite conditions or pattern matching, use
<code>RewriteRule</code> exclusively. If a simple prefix redirect
suffices, use <code>Redirect</code> and don't add rewrite rules
that might interact with it.</p>
</section>
<section id="encoding"><title>URL Encoding and Decoding</title>
<p>Apache httpd unescapes URL-encoded characters in the request URL-path before any
<directive module="mod_rewrite">RewriteRule</directive> pattern
matching takes place. A request for
<code>/my%20page/cats%3Fdogs</code> is decoded to
<code>/my page/cats?dogs</code>, and that decoded string is what the
<code>RewriteRule</code> pattern matches against.</p>
<p>This means you cannot write a pattern that matches the literal
URL-encoded form. If you need to distinguish
<code>/horses%2Fponies</code> from <code>/horses/ponies</code>, use
<code>%{THE_REQUEST}</code> in a <directive
module="mod_rewrite">RewriteCond</directive> — it preserves the
original request line exactly as the client sent it, before any
decoding:</p>
<highlight language="config">
# Match only the literally-encoded %2F, not a real path separator
RewriteCond "%{THE_REQUEST}" "/horses%2F"
RewriteRule "^/horses/ponies$" "/special-handler" [L]
</highlight>
<p>After substitution, <module>mod_rewrite</module> re-encodes the
resulting URL-path for output. Several flags control this behavior:</p>
<ul>
<li><a href="flags.html#flag_b">[B]</a> — re-escape
backreferences so that special characters captured from the
decoded URL-path are not interpreted as delimiters in the
substitution.</li>
<li><a href="flags.html#flag_bnp">[BNP]</a> — when [B] is
active, encode spaces as <code>%20</code> rather than
<code>+</code> (appropriate for path components, not query
strings).</li>
<li><a href="flags.html#flag_ne">[NE]</a> — suppress the
default escaping of special characters in the substitution
result, allowing literal <code>#</code>, <code>?</code>, and
other characters to pass through unmodified on external
redirects.</li>
</ul>
<section id="allowencodedslashes">
<title>AllowEncodedSlashes</title>
<p>By default, Apache returns 404 for any URL containing an encoded
slash (<code>%2F</code>). The <directive
module="core">AllowEncodedSlashes</directive> directive controls
this behavior:</p>
<ul>
<li><code>Off</code> (default) — reject <code>%2F</code> with
404.</li>
<li><code>On</code> — allow <code>%2F</code> and decode it to
<code>/</code> before passing to handlers.</li>
<li><code>NoDecode</code> — allow <code>%2F</code> but keep it
in encoded form, letting the backend application distinguish it
from a real path separator.</li>
</ul>
<p>When using the <a href="flags.html#flag_b">[B]</a> flag with
URLs that may contain encoded slashes, you typically need
<code>AllowEncodedSlashes NoDecode</code> to prevent Apache from
rejecting the re-encoded result.</p>
</section>
</section>
<section id="InternalRuleset"><title>Ruleset Processing</title>
<p>Now when <module>mod_rewrite</module> is triggered in these two API phases, it
reads the configured rulesets from its configuration
structure (which itself was either created on startup for
per-server context or during the directory walk of the Apache
kernel for per-directory context). Then the URL rewriting
engine is started with the contained ruleset (one or more
rules together with their conditions). The operation of the
URL rewriting engine itself is exactly the same for both
configuration contexts. Only the final result processing is
different.</p>
<p class="figure">
<img src="../images/mod_rewrite_fig1.png"
alt="Overview of per-request rewrite processing through
per-server and per-directory phases" /><br />
<dfn>Figure 1:</dfn>The per-request rewrite process showing
both per-server and per-directory rule processing phases
</p>
<p>The order of rules in the ruleset is important because the
rewriting engine processes them in a special (and not very
obvious) order. The rule is this: The rewriting engine loops
through the ruleset rule by rule (<directive
module="mod_rewrite">RewriteRule</directive> directives) and
when a particular rule matches it optionally loops through
existing corresponding conditions (<code>RewriteCond</code>
directives). For historical reasons the conditions are given
first, and so the control flow is a little bit long-winded. See
Figure 2 for more details.</p>
<p class="figure">
<img src="../images/rewrite_process_uri.png"
alt="Flowchart showing per-rule control flow: for each rule,
check pattern against URL, evaluate RewriteCond conditions,
apply substitution if both pass, then check flags to decide
whether to stop or continue to the next rule" /><br />
<dfn>Figure 2:</dfn>The control flow through the rewriting ruleset
</p>
<p>First the URL is matched against the
<em>Pattern</em> of each rule. If it fails, <module>mod_rewrite</module>
immediately stops processing this rule, and continues with the
next rule. If the <em>Pattern</em> matches, <module>mod_rewrite</module> looks
for corresponding rule conditions (RewriteCond directives,
appearing immediately above the RewriteRule in the configuration).
If none are present, it substitutes the URL with a new value, which is
constructed from the string <em>Substitution</em>, and goes on
with its rule-looping. But if conditions exist, it starts an
inner loop for processing them in the order that they are
listed. For conditions, the logic is different: we don't match
a pattern against the current URL. Instead we first create a
string <em>TestString</em> by expanding variables,
back-references, map lookups, <em>etc.</em> and then we try
to match <em>CondPattern</em> against it. If the pattern
doesn't match, the complete set of conditions and the
corresponding rule fails. If the pattern matches, then the
next condition is processed until no more conditions are
available. If all conditions match, processing is continued
with the substitution of the URL with
<em>Substitution</em>.</p>
<p class="figure">
<img src="../images/mod_rewrite_fig2.png"
alt="Flow of backreferences between RewriteRule and
RewriteCond directives" /><br />
<dfn>Figure 3:</dfn>The flow of backreferences through a rule.
RewriteRule Pattern is matched first; its captures ($1...$9)
are available in all RewriteCond TestStrings. The last matched
CondPattern's captures (%1...%9) are available in Substitution.
</p>
</section>
</manualpage>