| <!DOCTYPE html SYSTEM "about:legacy-compat"> | |
| <html lang="en"><head><META http-equiv="Content-Type" content="text/html; charset=UTF-8"> | |
| <meta content="width=device-width, initial-scale=1" name="viewport"> | |
| <!-- | |
| XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX | |
| This file is generated from xml source: DO NOT EDIT | |
| XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX | |
| --> | |
| <title>Apache mod_rewrite Technical Details - Apache HTTP Server Version 2.5</title> | |
| <link href="../style/css/manual.css" rel="stylesheet" media="all" type="text/css" title="Main stylesheet"> | |
| <link href="../style/css/manual-loose-100pc.css" rel="alternate stylesheet" media="all" type="text/css" title="No Sidebar - Default font size"> | |
| <link href="../style/css/manual-print.css" rel="stylesheet" media="print" type="text/css"><link rel="stylesheet" type="text/css" href="../style/css/prettify.css"> | |
| <script src="../style/scripts/prettify.min.js"> | |
| </script> | |
| <link href="../images/favicon.png" rel="shortcut icon"></head> | |
| <body id="manual-page"><div id="page-header"> | |
| <p class="menu"><a href="../mod/">Modules</a> | <a href="../mod/quickreference.html">Directives</a> | <a href="https://cwiki.apache.org/confluence/display/httpd/FAQ">FAQ</a> | <a href="../glossary.html">Glossary</a> | <a href="../sitemap.html">Sitemap</a> | <a href="https://bz.apache.org/bugzilla/enter_bug.cgi?product=Apache%20httpd-2">Report a bug</a></p> | |
| <p class="apache">Apache HTTP Server Version 2.5</p> | |
| <img alt="" src="../images/feather.png"></div> | |
| <div class="up"><a href="./"><img title="<-" alt="<-" src="../images/left.gif"></a></div> | |
| <div id="path"> | |
| <a href="https://www.apache.org/">Apache</a> > <a href="https://httpd.apache.org/">HTTP Server</a> > <a href="https://httpd.apache.org/docs/">Documentation</a> > <a href="../">Version 2.5</a> > <a href="./">Rewrite</a></div><div id="page-content"><div id="preamble"><h1>Apache mod_rewrite Technical Details</h1> | |
| <button aria-label="Toggle language list" class="lang-toggle"><svg xmlns="http://www.w3.org/2000/svg" stroke-width="2" stroke="currentColor" fill="none" viewBox="0 0 24 24" height="16" width="16"><circle r="10" cy="12" cx="12"/><line y2="12" x2="22" y1="12" x1="2"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg></button> | |
| <div class="toplang"> | |
| <p><span>Available Languages: </span><a href="../de/rewrite/tech.html" hreflang="de" rel="alternate" title="Deutsch"> de </a> | | |
| <a href="../en/rewrite/tech.html" title="English"> en </a> | | |
| <a href="../es/rewrite/tech.html" hreflang="es" rel="alternate" title="Español"> es </a> | | |
| <a href="../fr/rewrite/tech.html" hreflang="fr" rel="alternate" title="Français"> fr </a> | | |
| <a href="../ja/rewrite/tech.html" hreflang="ja" rel="alternate" title="Japanese"> ja </a> | | |
| <a href="../ko/rewrite/tech.html" hreflang="ko" rel="alternate" title="Korean"> ko </a> | | |
| <a href="../tr/rewrite/tech.html" hreflang="tr" rel="alternate" title="Türkçe"> tr </a> | | |
| <a href="../zh-cn/rewrite/tech.html" hreflang="zh-cn" rel="alternate" title="Simplified Chinese"> zh-cn </a></p> | |
| </div> | |
| <p>This document discusses some of the technical details of <code class="module"><a href="../mod/mod_rewrite.html">mod_rewrite</a></code> | |
| and URL matching.</p> | |
| </div> | |
| <div id="quickview"><ul id="toc"><li><img alt="" src="../images/down.gif"> <a href="#InternalAPI">API Phases</a></li> | |
| <li><img alt="" src="../images/down.gif"> <a href="#order">Module Processing Order</a></li> | |
| <li><img alt="" src="../images/down.gif"> <a href="#encoding">URL Encoding and Decoding</a></li> | |
| <li><img alt="" src="../images/down.gif"> <a href="#InternalRuleset">Ruleset Processing</a></li> | |
| </ul><h3>See also</h3><ul class="seealso"><li><a href="../mod/mod_rewrite.html">Module documentation</a></li><li><a href="intro.html">mod_rewrite introduction</a></li><li><a href="remapping.html">Redirection and remapping</a></li><li><a href="flags.html">RewriteRule Flags</a></li><li><a href="vhosts.html">Virtual hosts</a></li><li><a href="rewritemap.html">Using RewriteMap</a></li><li><a href="htaccess.html">Per-directory Rewrites</a></li><li><a href="avoid.html">When not to use mod_rewrite</a></li></ul></div> | |
| <div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif"></a></div> | |
| <div class="section"> | |
| <h2 id="InternalAPI">API Phases <a title="Permanent link" href="#InternalAPI" class="permalink">¶</a></h2> | |
| <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><code class="module"><a href="../mod/mod_rewrite.html">mod_rewrite</a></code> 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 <code class="module"><a href="../mod/mod_rewrite.html">mod_rewrite</a></code> directives appearing in the | |
| per-server configuration. (i.e., in the main server configuration file | |
| and <code class="directive"><a href="../mod/core.html#virtualhost"><Virtualhost></a></code> | |
| 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 <code class="directive"><a href="../mod/core.html#directory"><Directory></a></code> blocks) are applied. This | |
| happens in the Fixup phase.</p> | |
| <p>In each of these cases, <code class="module"><a href="../mod/mod_rewrite.html">mod_rewrite</a></code> rewrites the | |
| <code>REQUEST_URI</code> either to a new URL, or to a filename.</p> | |
| <p>In <a class="glossarylink" href="../glossary.html#perdirectory" title="see glossary">per-directory context</a>, | |
| 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> | |
| </div><div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif"></a></div> | |
| <div class="section"> | |
| <h2 id="order">Module Processing Order <a title="Permanent link" href="#order" class="permalink">¶</a></h2> | |
| <p><code class="module"><a href="../mod/mod_rewrite.html">mod_rewrite</a></code> and <code class="module"><a href="../mod/mod_alias.html">mod_alias</a></code> both | |
| operate during the URL-to-filename translation phase, but | |
| <code class="module"><a href="../mod/mod_rewrite.html">mod_rewrite</a></code> 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 <code class="directive"><a href="../mod/mod_rewrite.html#rewriterule">RewriteRule</a></code> and <code class="directive"><a href="../mod/mod_alias.html#redirect">Redirect</a></code> (or <code class="directive"><a href="../mod/mod_alias.html#redirectmatch">RedirectMatch</a></code>) 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> | |
| <pre class="prettyprint lang-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]</pre> | |
| <div class="note"><h3>Per-directory context reverses the order</h3> | |
| <p>In <a class="glossarylink" href="../glossary.html#perdirectory" title="see glossary">per-directory context</a>, | |
| the situation is different. <code class="module"><a href="../mod/mod_alias.html">mod_alias</a></code> directives like | |
| <code>Redirect</code> still run in the URL-to-filename translation | |
| phase, but <code class="module"><a href="../mod/mod_rewrite.html">mod_rewrite</a></code> 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> | |
| </div> | |
| <p>Because of this inconsistency between contexts, mixing | |
| <code class="module"><a href="../mod/mod_rewrite.html">mod_rewrite</a></code> and <code class="module"><a href="../mod/mod_alias.html">mod_alias</a></code> | |
| 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> | |
| </div><div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif"></a></div> | |
| <div class="section"> | |
| <h2 id="encoding">URL Encoding and Decoding <a title="Permanent link" href="#encoding" class="permalink">¶</a></h2> | |
| <p>Apache httpd unescapes URL-encoded characters in the request URL-path before any | |
| <code class="directive"><a href="../mod/mod_rewrite.html#rewriterule">RewriteRule</a></code> 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 <code class="directive"><a href="../mod/mod_rewrite.html#rewritecond">RewriteCond</a></code> — it preserves the | |
| original request line exactly as the client sent it, before any | |
| decoding:</p> | |
| <pre class="prettyprint lang-config"># Match only the literally-encoded %2F, not a real path separator | |
| RewriteCond "%{THE_REQUEST}" "/horses%2F" | |
| RewriteRule "^/horses/ponies$" "/special-handler" [L]</pre> | |
| <p>After substitution, <code class="module"><a href="../mod/mod_rewrite.html">mod_rewrite</a></code> 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> | |
| <h3 id="allowencodedslashes">AllowEncodedSlashes</h3> | |
| <p>By default, Apache returns 404 for any URL containing an encoded | |
| slash (<code>%2F</code>). The <code class="directive"><a href="../mod/core.html#allowencodedslashes">AllowEncodedSlashes</a></code> 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> | |
| </div><div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif"></a></div> | |
| <div class="section"> | |
| <h2 id="InternalRuleset">Ruleset Processing <a title="Permanent link" href="#InternalRuleset" class="permalink">¶</a></h2> | |
| <p>Now when <code class="module"><a href="../mod/mod_rewrite.html">mod_rewrite</a></code> 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 (<code class="directive"><a href="../mod/mod_rewrite.html#rewriterule">RewriteRule</a></code> 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, <code class="module"><a href="../mod/mod_rewrite.html">mod_rewrite</a></code> | |
| immediately stops processing this rule, and continues with the | |
| next rule. If the <em>Pattern</em> matches, <code class="module"><a href="../mod/mod_rewrite.html">mod_rewrite</a></code> 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> | |
| </div></div> | |
| <div class="bottomlang"> | |
| <p><span>Available Languages: </span><a href="../de/rewrite/tech.html" hreflang="de" rel="alternate" title="Deutsch"> de </a> | | |
| <a href="../en/rewrite/tech.html" title="English"> en </a> | | |
| <a href="../es/rewrite/tech.html" hreflang="es" rel="alternate" title="Español"> es </a> | | |
| <a href="../fr/rewrite/tech.html" hreflang="fr" rel="alternate" title="Français"> fr </a> | | |
| <a href="../ja/rewrite/tech.html" hreflang="ja" rel="alternate" title="Japanese"> ja </a> | | |
| <a href="../ko/rewrite/tech.html" hreflang="ko" rel="alternate" title="Korean"> ko </a> | | |
| <a href="../tr/rewrite/tech.html" hreflang="tr" rel="alternate" title="Türkçe"> tr </a> | | |
| <a href="../zh-cn/rewrite/tech.html" hreflang="zh-cn" rel="alternate" title="Simplified Chinese"> zh-cn </a></p> | |
| </div><div id="footer"> | |
| <p class="apache">Copyright 2026 The Apache Software Foundation.<br>Licensed under the <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache License, Version 2.0</a>.</p> | |
| <p class="menu"><a href="../mod/">Modules</a> | <a href="../mod/quickreference.html">Directives</a> | <a href="https://cwiki.apache.org/confluence/display/httpd/FAQ">FAQ</a> | <a href="../glossary.html">Glossary</a> | <a href="../sitemap.html">Sitemap</a> | <a href="https://bz.apache.org/bugzilla/enter_bug.cgi?product=Apache%20httpd-2">Report a bug</a></p></div><script><!--//--><![CDATA[//><!-- | |
| if (typeof(prettyPrint) !== 'undefined') { | |
| prettyPrint(); | |
| } | |
| var langToggle = document.querySelector('.lang-toggle'); | |
| var topLang = document.querySelector('.toplang'); | |
| if (langToggle && topLang) { | |
| langToggle.addEventListener('click', function() { topLang.classList.toggle('open'); }); | |
| } | |
| var qv = document.getElementById('quickview'); | |
| if (qv) { | |
| document.body.appendChild(qv); | |
| var qvBtn = document.createElement('button'); | |
| qvBtn.className = 'qv-toggle'; | |
| qvBtn.setAttribute('aria-label', 'Toggle page navigation'); | |
| qvBtn.innerHTML = '☰'; | |
| document.body.appendChild(qvBtn); | |
| qvBtn.addEventListener('click', function() { | |
| var isOpen = qv.classList.toggle('open'); | |
| if (isOpen) { | |
| qv.style.top = window.scrollY + 10 + 'px'; | |
| } | |
| }); | |
| window.addEventListener('scroll', function() { qv.classList.remove('open'); }); | |
| } | |
| //--><!]]></script> | |
| </body></html> |