| <?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="remapping.xml.meta"> | |
| <parentdocument href="./">Rewrite</parentdocument> | |
| <title>Redirecting and Remapping with mod_rewrite</title> | |
| <summary> | |
| <p>This document supplements the <module>mod_rewrite</module> | |
| <a href="../mod/mod_rewrite.html">reference documentation</a>. It describes | |
| how you can use <module>mod_rewrite</module> to redirect and remap | |
| request. This includes many examples of common uses of <module>mod_rewrite</module>, | |
| including detailed descriptions of how each works.</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="htaccess.html">Per-directory Rewrites</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="avoid.html">When not to use mod_rewrite</a></seealso> | |
| <seealso><a href="tech.html">Technical details</a></seealso> | |
| <section id="old-to-new"> | |
| <title>From Old to New (internal)</title> | |
| <dl> | |
| <dt>Description:</dt> | |
| <dd> | |
| <p>Assume we have recently renamed the page | |
| <code>foo.html</code> to <code>bar.html</code> and now want | |
| to provide the old URL for backward compatibility. However, | |
| we want that users of the old URL even not recognize that | |
| the pages was renamed - that is, we don't want the address to | |
| change in their browser.</p> | |
| </dd> | |
| <dt>Solution:</dt> | |
| <dd> | |
| <p>We rewrite the old URL to the new one internally via the | |
| following rule:</p> | |
| <highlight language="config"> | |
| RewriteEngine on | |
| RewriteRule "^<strong>/foo</strong>\.html$" "<strong>/bar</strong>.html" [PT] | |
| </highlight> | |
| </dd> | |
| </dl> | |
| </section> | |
| <section id="https-redirect"> | |
| <title>Forcing HTTPS</title> | |
| <dl> | |
| <dt>Description:</dt> | |
| <dd> | |
| <p>You want all HTTP requests to be redirected to HTTPS. This | |
| is one of the most common uses of <module>mod_rewrite</module>, | |
| but in most cases it is better accomplished without it.</p> | |
| </dd> | |
| <dt>Solution:</dt> | |
| <dd> | |
| <p>The preferred approach uses a | |
| <directive module="mod_alias">Redirect</directive> directive in a | |
| dedicated HTTP virtual host:</p> | |
| <highlight language="config"> | |
| <VirtualHost *:80> | |
| ServerName www.example.com | |
| Redirect permanent "/" "https://www.example.com/" | |
| </VirtualHost> | |
| <VirtualHost *:443> | |
| ServerName www.example.com | |
| # ... SSL configuration goes here | |
| </VirtualHost> | |
| </highlight> | |
| </dd> | |
| <dt>Discussion:</dt> | |
| <dd> | |
| <p>If you do not have access to the main server configuration and | |
| must use a <code>.htaccess</code> file, <module>mod_rewrite</module> | |
| is the appropriate tool:</p> | |
| <highlight language="config"> | |
| RewriteEngine On | |
| RewriteCond "%{HTTPS}" !=on | |
| RewriteRule "^(.*)" "https://%{SERVER_NAME}$1" [R=301,L] | |
| </highlight> | |
| <p>The <code>%{HTTPS}</code> variable is set to <code>on</code> | |
| when the connection is using SSL/TLS, and is empty or | |
| <code>off</code> otherwise. Using <code>R=301</code> issues a | |
| permanent redirect, which tells search engines to update their | |
| index.</p> | |
| <p>See also the <a href="avoid.html#redirect">When not to use | |
| mod_rewrite</a> document for more discussion of the | |
| <code>Redirect</code> approach.</p> | |
| <note><title>Behind a load balancer or SSL terminator</title> | |
| <p>The <code>%{HTTPS}</code> variable is not a general-purpose | |
| environment variable — it queries <module>mod_ssl</module> | |
| directly. If SSL/TLS is terminated at an upstream load balancer | |
| or reverse proxy, <module>mod_ssl</module> is not handling the | |
| connection and <code>%{HTTPS}</code> will always report | |
| <code>off</code>, even when the original client connected over | |
| HTTPS.</p> | |
| <p>In this situation, check the header set by the upstream proxy | |
| instead. Most load balancers set | |
| <code>X-Forwarded-Proto</code>:</p> | |
| </note> | |
| <highlight language="config"> | |
| RewriteEngine On | |
| RewriteCond "%{HTTP:X-Forwarded-Proto}" =http [NC] | |
| RewriteRule "^(.*)" "https://%{SERVER_NAME}$1" [R=301,L] | |
| </highlight> | |
| <note type="warning"> | |
| <p>Only trust <code>X-Forwarded-Proto</code> if you control the | |
| upstream proxy and it overwrites the header on every request. An | |
| attacker can forge this header when connecting directly to your | |
| server. Consider restricting access so that only your load | |
| balancer can reach the backend, or use | |
| <module>mod_remoteip</module> to validate the source.</p> | |
| </note> | |
| </dd> | |
| </dl> | |
| </section> | |
| <section id="acme-exemption"> | |
| <title>Exempting ACME challenge requests from HTTPS redirect</title> | |
| <dl> | |
| <dt>Description:</dt> | |
| <dd> | |
| <p>You have forced all traffic to HTTPS (as above), but your | |
| ACME client (Let's Encrypt, Certbot, etc.) needs plain HTTP | |
| access to <code>/.well-known/acme-challenge/</code> to complete | |
| domain validation.</p> | |
| </dd> | |
| <dt>Solution:</dt> | |
| <dd> | |
| <p>Place an exception <em>before</em> your HTTPS redirect | |
| rule:</p> | |
| <highlight language="config"> | |
| RewriteEngine On | |
| RewriteRule "^/\.well-known/acme-challenge/" - [L] | |
| RewriteCond "%{HTTPS}" !=on | |
| RewriteRule "^(.*)" "https://%{SERVER_NAME}$1" [R=301,L] | |
| </highlight> | |
| </dd> | |
| <dt>Discussion:</dt> | |
| <dd> | |
| <p>The dash (<code>-</code>) substitution means "do not rewrite." | |
| Combined with <code>[L]</code>, it stops rule processing for any | |
| request matching the ACME challenge path, allowing it to be | |
| served over plain HTTP. All other requests continue to the | |
| next rule and are redirected to HTTPS as usual.</p> | |
| <p>If you are using the <directive | |
| module="mod_alias">Redirect</directive> approach in a dedicated | |
| port-80 VirtualHost, use an | |
| <directive module="mod_alias">Alias</directive> and | |
| <directive module="mod_alias">RedirectMatch</directive> | |
| instead:</p> | |
| <highlight language="config"> | |
| <VirtualHost *:80> | |
| ServerName www.example.com | |
| # Allow ACME challenges over HTTP | |
| Alias "/.well-known/acme-challenge/" "/var/www/acme/.well-known/acme-challenge/" | |
| <Directory "/var/www/acme/.well-known/acme-challenge"> | |
| Require all granted | |
| </Directory> | |
| # Everything else goes to HTTPS | |
| RedirectMatch permanent "^/(?!\.well-known/acme-challenge/)(.*)$" "https://www.example.com/$1" | |
| </VirtualHost> | |
| </highlight> | |
| </dd> | |
| </dl> | |
| </section> | |
| <section id="trailing-slash"> | |
| <title>Trailing Slash Normalization</title> | |
| <dl> | |
| <dt>Description:</dt> | |
| <dd> | |
| <p>You want to ensure that URLs for directories always end with | |
| a trailing slash, or conversely, that they never do. This is a | |
| common requirement for SEO and for consistent URL handling by | |
| web applications.</p> | |
| </dd> | |
| <dt>Solution:</dt> | |
| <dd> | |
| <p>To add a trailing slash to URLs that map to directories:</p> | |
| <highlight language="config"> | |
| RewriteCond "%{REQUEST_FILENAME}" -d | |
| RewriteCond "%{REQUEST_URI}" "!/$" | |
| RewriteRule "^(.*)$" "$1/" [R=301,L] | |
| </highlight> | |
| <p>To remove a trailing slash (except for actual directories):</p> | |
| <highlight language="config"> | |
| RewriteCond "%{REQUEST_FILENAME}" !-d | |
| RewriteCond "%{REQUEST_URI}" "(.+)/$" | |
| RewriteRule "^" "%1" [R=301,L] | |
| </highlight> | |
| </dd> | |
| <dt>Discussion:</dt> | |
| <dd> | |
| <p>Apache's <module>mod_dir</module> already handles trailing | |
| slash redirects for real directories when | |
| <directive module="mod_dir">DirectorySlash</directive> is enabled | |
| (the default). You only need a <module>mod_rewrite</module> rule | |
| if you want to enforce trailing slash behavior for URLs that do | |
| not correspond to actual directories on disk, or if you want to | |
| remove trailing slashes.</p> | |
| </dd> | |
| </dl> | |
| </section> | |
| <section id="front-controller"> | |
| <title>Front Controller / Application Routing</title> | |
| <p>Most modern web frameworks route all requests through a single | |
| entry point (a "front controller"). The | |
| <directive module="mod_dir">FallbackResource</directive> directive | |
| handles this more simply and efficiently than | |
| <module>mod_rewrite</module>. See <a | |
| href="avoid.html#fallback-resource">When NOT to use mod_rewrite</a> | |
| for the recommended approach.</p> | |
| <p>If you genuinely need <module>mod_rewrite</module> for this (for | |
| example, to add conditions beyond "file doesn't exist"), see the | |
| <a href="htaccess.html#rewritebase">per-directory rewrites</a> | |
| document for an annotated example that also demonstrates | |
| <directive module="mod_rewrite">RewriteBase</directive> usage.</p> | |
| </section> | |
| <section id="old-to-new-extern"> | |
| <title>Rewriting From Old to New (external)</title> | |
| <dl> | |
| <dt>Description:</dt> | |
| <dd> | |
| <p>Assume again that we have recently renamed the page | |
| <code>foo.html</code> to <code>bar.html</code> and now want | |
| to provide the old URL for backward compatibility. But this | |
| time we want that the users of the old URL get hinted to | |
| the new one, i.e. their browsers Location field should | |
| change, too.</p> | |
| </dd> | |
| <dt>Solution:</dt> | |
| <dd> | |
| <p>We force a HTTP redirect to the new URL which leads to a | |
| change of the browsers and thus the users view:</p> | |
| <highlight language="config"> | |
| RewriteEngine on | |
| RewriteRule "^<strong>/foo</strong>\.html$" "<strong>bar</strong>.html" [<strong>R</strong>] | |
| </highlight> | |
| </dd> | |
| <dt>Discussion</dt> | |
| <dd> | |
| <p>In this example, as contrasted to the <a | |
| href="#old-to-new-intern">internal</a> example above, we can simply | |
| use the Redirect directive. <module>mod_rewrite</module> was used in that earlier | |
| example in order to hide the redirect from the client:</p> | |
| <highlight language="config"> | |
| Redirect "/foo.html" "/bar.html" | |
| </highlight> | |
| </dd> | |
| </dl> | |
| </section> | |
| <section id="movehomedirs"> | |
| <title>Resource Moved to Another Server</title> | |
| <dl> | |
| <dt>Description:</dt> | |
| <dd> | |
| <p>If a resource has moved to another server, you may wish to have | |
| URLs continue to work for a time on the old server while people | |
| update their bookmarks.</p> | |
| </dd> | |
| <dt>Solution:</dt> | |
| <dd> | |
| <p>You can use <module>mod_rewrite</module> to redirect these URLs | |
| to the new server, but you might also consider using the Redirect | |
| or RedirectMatch directive.</p> | |
| <highlight language="config"> | |
| #With mod_rewrite | |
| RewriteEngine on | |
| RewriteRule "^/docs/(.+)" "http://new.example.com/docs/$1" [R,L] | |
| </highlight> | |
| <highlight language="config"> | |
| #With RedirectMatch | |
| RedirectMatch "^/docs/(.*)" "http://new.example.com/docs/$1" | |
| </highlight> | |
| <highlight language="config"> | |
| #With Redirect | |
| Redirect "/docs/" "http://new.example.com/docs/" | |
| </highlight> | |
| </dd> | |
| <dt>Discussion:</dt> | |
| <dd> | |
| <p>For simple redirections to another server, the | |
| <directive module="mod_alias">Redirect</directive> or | |
| <directive module="mod_alias">RedirectMatch</directive> directives | |
| are preferred, as they are simpler and more efficient.</p> | |
| </dd> | |
| </dl> | |
| </section> | |
| <section id="backward-compatibility"> | |
| <title>Backward Compatibility for file extension change</title> | |
| <dl> | |
| <dt>Description:</dt> | |
| <dd> | |
| <p>How can we make URLs backward compatible (still | |
| existing virtually) after migrating <code>document.YYYY</code> | |
| to <code>document.XXXX</code>, e.g. after translating a | |
| bunch of <code>.html</code> files to <code>.php</code>?</p> | |
| </dd> | |
| <dt>Solution:</dt> | |
| <dd> | |
| <p>The URL is rewritten from the old extension to the new | |
| one only if the target file with the new extension exists | |
| and the original file with the old extension does not. | |
| Otherwise, the URL is left unchanged.</p> | |
| <highlight language="config"> | |
| # backward compatibility ruleset for | |
| # rewriting document.html to document.php | |
| # when and only when document.php exists | |
| <Directory "/var/www/htdocs"> | |
| RewriteEngine on | |
| RewriteBase "/var/www/htdocs" | |
| RewriteCond "$1.php" -f | |
| RewriteCond "$1.html" !-f | |
| RewriteRule "^(.*).html$" "$1.php" | |
| </Directory> | |
| </highlight> | |
| </dd> | |
| <dt>Discussion</dt> | |
| <dd> | |
| <p>This example uses an often-overlooked feature of <module>mod_rewrite</module>, | |
| by taking advantage of the order of execution of the ruleset. In | |
| particular, <module>mod_rewrite</module> evaluates the left-hand-side of the | |
| RewriteRule before it evaluates the RewriteCond directives. | |
| Consequently, $1 is already defined by the time the RewriteCond | |
| directives are evaluated. This allows us to test for the existence | |
| of the original (<code>document.html</code>) and target | |
| (<code>document.php</code>) files using the same base filename.</p> | |
| <p>This ruleset is designed to use in a per-directory context (In a | |
| <Directory> block or in a .htaccess file), so that the | |
| <code>-f</code> checks are looking at the correct directory path. | |
| You may need to set a <directive | |
| module="mod_rewrite">RewriteBase</directive> directive to specify the | |
| directory base that you're working in.</p> | |
| </dd> | |
| </dl> | |
| </section> | |
| <section id="canonicalhost"> | |
| <title>Canonical Hostnames</title> | |
| <dl> | |
| <dt>Description:</dt> | |
| <dd>The goal of this rule is to force the use of a particular | |
| hostname, in preference to other hostnames which may be used to | |
| reach the same site. For example, if you wish to force the use | |
| of <strong>www.example.com</strong> instead of | |
| <strong>example.com</strong>, you might use a variant of the | |
| following recipe.</dd> | |
| <dt>Solution:</dt> | |
| <dd> | |
| <p>The very best way to solve this doesn't involve <module>mod_rewrite</module> at all, | |
| but rather uses the <directive module="mod_alias">Redirect</directive> | |
| directive placed in a virtual host for the non-canonical | |
| hostname(s).</p> | |
| <highlight language="config"> | |
| <VirtualHost *:80> | |
| ServerName undesired.example.com | |
| ServerAlias example.com notthis.example.com | |
| Redirect "/" "http://www.example.com/" | |
| </VirtualHost> | |
| <VirtualHost *:80> | |
| ServerName www.example.com | |
| </VirtualHost> | |
| </highlight> | |
| <p>You can alternatively accomplish this using the | |
| <directive module="core" type="section">If</directive> | |
| directive: (<strong>2.4 and later</strong>)</p> | |
| <highlight language="config"> | |
| <If "%{HTTP_HOST} != 'www.example.com'"> | |
| Redirect "/" "http://www.example.com/" | |
| </If> | |
| </highlight> | |
| <p>Or, for example, to redirect a portion of your site to HTTPS, you | |
| might do the following:</p> | |
| <highlight language="config"> | |
| <If "%{SERVER_PROTOCOL} != 'HTTPS'"> | |
| Redirect "/admin/" "https://www.example.com/admin/" | |
| </If> | |
| </highlight> | |
| <p>If, for whatever reason, you still want to use <module>mod_rewrite</module> | |
| - if, for example, you need this to work with a larger set of RewriteRules - | |
| you might use one of the recipes below.</p> | |
| <p>For sites running on a port other than 80:</p> | |
| <highlight language="config"> | |
| RewriteCond "%{HTTP_HOST}" "!^www\.example\.com" [NC] | |
| RewriteCond "%{HTTP_HOST}" "!^$" | |
| RewriteCond "%{SERVER_PORT}" "!^80$" | |
| RewriteRule "^/?(.*)" "http://www.example.com:%{SERVER_PORT}/$1" [L,R,NE] | |
| </highlight> | |
| <p>And for a site running on port 80</p> | |
| <highlight language="config"> | |
| RewriteCond "%{HTTP_HOST}" "!^www\.example\.com" [NC] | |
| RewriteCond "%{HTTP_HOST}" "!^$" | |
| RewriteRule "^/?(.*)" "http://www.example.com/$1" [L,R,NE] | |
| </highlight> | |
| <p> | |
| If you wanted to do this generically for all domain names - that | |
| is, if you want to redirect <strong>example.com</strong> to | |
| <strong>www.example.com</strong> for all possible values of | |
| <strong>example.com</strong>, you could use the following | |
| recipe:</p> | |
| <p>To do the reverse - strip the <code>www.</code> prefix - swap the | |
| condition:</p> | |
| <highlight language="config"> | |
| RewriteCond "%{HTTP_HOST}" "^www\.(.+)$" [NC] | |
| RewriteRule "^(.*)" "http://%1/$1" [L,R,NE] | |
| </highlight> | |
| <p>To generically add <code>www.</code> to any hostname:</p> | |
| <highlight language="config"> | |
| RewriteCond "%{HTTP_HOST}" "!^www\." [NC] | |
| RewriteCond "%{HTTP_HOST}" "!^$" | |
| RewriteRule "^/?(.*)" "http://www.%{HTTP_HOST}/$1" [L,R,NE] | |
| </highlight> | |
| <p>These rulesets will work either in your main server configuration | |
| file, or in a <code>.htaccess</code> file placed in the <directive | |
| module="core">DocumentRoot</directive> of the server.</p> | |
| </dd> | |
| <dt>Discussion:</dt> | |
| <dd> | |
| <p>If you have access to the server configuration, a | |
| <directive module="mod_alias">Redirect</directive> in a dedicated | |
| <directive module="core" type="section">VirtualHost</directive> | |
| is the cleanest approach. Canonicalizing the hostname ensures that | |
| search engines treat your site as a single entity and avoids | |
| cookie scope issues that arise when the same site is reachable | |
| under multiple names.</p> | |
| <p>Use the | |
| <directive module="core" type="section">If</directive> directive | |
| as a middle ground, and <module>mod_rewrite</module> only if you | |
| are limited to <code>.htaccess</code>.</p> | |
| </dd> | |
| </dl> | |
| </section> | |
| <section id="multipledirs"> | |
| <title>Search for pages in more than one directory</title> | |
| <dl> | |
| <dt>Description:</dt> | |
| <dd> | |
| <p>A particular resource might exist in one of several places, and | |
| we want to look in those places for the resource when it is | |
| requested. Perhaps we've recently rearranged our directory | |
| structure, dividing content into several locations.</p> | |
| </dd> | |
| <dt>Solution:</dt> | |
| <dd> | |
| <p>The following ruleset searches in two directories to find the | |
| resource, and, if not finding it in either place, will attempt to | |
| just serve it out of the location requested.</p> | |
| <highlight language="config"> | |
| RewriteEngine on | |
| # first try to find it in dir1/... | |
| # ...and if found stop and be happy: | |
| RewriteCond "%{DOCUMENT_ROOT}/<strong>dir1</strong>/%{REQUEST_URI}" -f | |
| RewriteRule "^(.+)" "%{DOCUMENT_ROOT}/<strong>dir1</strong>/$1" [L] | |
| # second try to find it in dir2/... | |
| # ...and if found stop and be happy: | |
| RewriteCond "%{DOCUMENT_ROOT}/<strong>dir2</strong>/%{REQUEST_URI}" -f | |
| RewriteRule "^(.+)" "%{DOCUMENT_ROOT}/<strong>dir2</strong>/$1" [L] | |
| # else go on for other Alias or ScriptAlias directives, | |
| # etc. | |
| RewriteRule "^" "-" [PT] | |
| </highlight> | |
| </dd> | |
| <dt>Discussion:</dt> | |
| <dd> | |
| <p>This is useful during migrations when content is being moved | |
| between directories. For permanent setups, consider using | |
| <directive module="mod_alias">Alias</directive> or symbolic links | |
| instead.</p> | |
| </dd> | |
| </dl> | |
| </section> | |
| <section id="canonicalurl"> | |
| <title>Canonical URLs</title> | |
| <dl> | |
| <dt>Description:</dt> | |
| <dd> | |
| <p>On some webservers there is more than one URL for a | |
| resource. Usually there are canonical URLs (which are be | |
| actually used and distributed) and those which are just | |
| shortcuts, internal ones, and so on. Independent of which URL the | |
| user supplied with the request, they should finally see the | |
| canonical one in their browser address bar.</p> | |
| </dd> | |
| <dt>Solution:</dt> | |
| <dd> | |
| <p>We do an external HTTP redirect for all non-canonical | |
| URLs to fix them in the location view of the Browser and | |
| for all subsequent requests. In the example ruleset below | |
| we replace <code>/puppies</code> and <code>/canines</code> | |
| by the canonical <code>/dogs</code>.</p> | |
| <highlight language="config"> | |
| RewriteRule "^/(puppies|canines)/(.*)" "/dogs/$2" [R] | |
| </highlight> | |
| </dd> | |
| <dt>Discussion:</dt> | |
| <dd> | |
| This should really be accomplished with Redirect or RedirectMatch | |
| directives: | |
| <highlight language="config"> | |
| RedirectMatch "^/(puppies|canines)/(.*)" "/dogs/$2" | |
| </highlight> | |
| </dd> | |
| </dl> | |
| </section> | |
| <section id="moveddocroot"> | |
| <title>Moved <code>DocumentRoot</code></title> | |
| <dl> | |
| <dt>Description:</dt> | |
| <dd> | |
| <p>Usually the <directive module="core">DocumentRoot</directive> | |
| of the webserver directly relates to the URL "<code>/</code>". | |
| But often this data is not really of top-level priority. For example, | |
| you may wish for visitors, on first entering a site, to go to a | |
| particular subdirectory <code>/about/</code>. This may be accomplished | |
| using the following ruleset:</p> | |
| </dd> | |
| <dt>Solution:</dt> | |
| <dd> | |
| <p>We redirect the URL <code>/</code> to | |
| <code>/about/</code>: | |
| </p> | |
| <highlight language="config"> | |
| RewriteEngine on | |
| RewriteRule "^/$" "/about/" [<strong>R</strong>] | |
| </highlight> | |
| <p>Note that this can also be handled using the <directive | |
| module="mod_alias">RedirectMatch</directive> directive:</p> | |
| <highlight language="config"> | |
| RedirectMatch "^/$" "http://example.com/about/" | |
| </highlight> | |
| <p>Note also that the example rewrites only the root URL. That is, it | |
| rewrites a request for <code>http://example.com/</code>, but not a | |
| request for <code>http://example.com/page.html</code>. If you have in | |
| fact changed your document root - that is, if <strong>all</strong> of | |
| your content is in fact in that subdirectory, it is greatly preferable | |
| to simply change your <directive module="core">DocumentRoot</directive> | |
| directive, or move all of the content up one directory, | |
| rather than rewriting URLs.</p> | |
| </dd> | |
| </dl> | |
| </section> | |
| <section id="rewrite-query"> | |
| <title>Rewrite query string</title> | |
| <dl> | |
| <dt>Description:</dt> | |
| <dd>You want to capture a particular value from a query string | |
| and either replace it or incorporate it into another component | |
| of the URL.</dd> | |
| <dt>Solutions:</dt> | |
| <dd> | |
| <p> Many of the solutions in this section will all use the same condition, | |
| which leaves the matched value in the %2 backreference. %1 is the beginning | |
| of the query string (up to the key of interest), and %3 is the remainder. This | |
| condition is a bit complex for flexibility and to avoid double '&&' in the | |
| substitutions.</p> | |
| <ul> | |
| <li>This solution removes the matching key and value: | |
| <highlight language="config"> | |
| # Remove mykey=??? | |
| RewriteCond "%{QUERY_STRING}" "(.*(?:^|&))mykey=([^&]*)&?(.*)&?$" | |
| RewriteRule "(.*)" "$1?%1%3" | |
| </highlight> | |
| </li> | |
| <li>This solution uses the captured value in the URL substitution, | |
| discarding the rest of the original query by appending a '?': | |
| <highlight language="config"> | |
| # Copy from query string to PATH_INFO | |
| RewriteCond "%{QUERY_STRING}" "(.*(?:^|&))mykey=([^&]*)&?(.*)&?$" | |
| RewriteRule "(.*)" "$1/products/%2/?" [PT] | |
| </highlight> | |
| </li> | |
| <li>This solution checks the captured value in a subsequent condition: | |
| <highlight language="config"> | |
| # Capture the value of mykey in the query string | |
| RewriteCond "%{QUERY_STRING}" "(.*(?:^|&))mykey=([^&]*)&?(.*)&?$" | |
| RewriteCond "%2" !=not-so-secret-value | |
| RewriteRule "(.*)" "-" [F] | |
| </highlight> | |
| </li> | |
| <li>This solution shows the reverse of the previous ones, copying | |
| path components (perhaps <glossary ref="pathinfo">PATH_INFO</glossary>) from the URL into the query string. | |
| <highlight language="config"> | |
| # The desired URL might be /products/kitchen-sink, and the script expects | |
| # /path?products=kitchen-sink. | |
| RewriteRule "^/?path/([^/]+)/([^/]+)" "/path?$1=$2" [PT] | |
| </highlight> | |
| </li> | |
| </ul> | |
| </dd> | |
| <dt>Discussion:</dt> | |
| <dd> | |
| <p>See also the <a href="flags.html#flag_qsa">[QSA]</a> and | |
| <a href="flags.html#flag_qsd">[QSD]</a> flags, which control whether | |
| the original query string is appended to or discarded from the | |
| substitution.</p> | |
| </dd> | |
| </dl> | |
| </section> | |
| <section id="structuredhomedirs"> | |
| <title>Structured Userdirs</title> | |
| <dl> | |
| <dt>Description:</dt> | |
| <dd> | |
| <p>Some sites with thousands of users use a | |
| structured homedir layout, <em>i.e.</em> each homedir is in a | |
| subdirectory which begins (for instance) with the first | |
| character of the username. So, <code>/~larry/anypath</code> | |
| is <code>/home/<strong>l</strong>/larry/public_html/anypath</code> | |
| while <code>/~waldo/anypath</code> is | |
| <code>/home/<strong>w</strong>/waldo/public_html/anypath</code>.</p> | |
| </dd> | |
| <dt>Solution:</dt> | |
| <dd> | |
| <p>We use the following ruleset to expand the tilde URLs | |
| into the above layout.</p> | |
| <highlight language="config"> | |
| RewriteEngine on | |
| RewriteRule "^/~(<strong>([a-z])</strong>[a-z0-9]+)(.*)" "/home/<strong>$2</strong>/$1/public_html$3" | |
| </highlight> | |
| </dd> | |
| <dt>Discussion:</dt> | |
| <dd> | |
| <p>This technique is primarily useful for large hosting | |
| environments with thousands of users. For most sites, | |
| <module>mod_userdir</module> handles tilde-based user URLs | |
| without requiring <module>mod_rewrite</module>.</p> | |
| </dd> | |
| </dl> | |
| </section> | |
| <section id="redirectanchors"> | |
| <title>Redirecting Anchors</title> | |
| <dl> | |
| <dt>Description:</dt> | |
| <dd> | |
| <p>By default, redirecting to an HTML anchor doesn't work, | |
| because <module>mod_rewrite</module> escapes the <code>#</code> character, | |
| turning it into <code>%23</code>. This, in turn, breaks the | |
| redirection.</p> | |
| </dd> | |
| <dt>Solution:</dt> | |
| <dd> | |
| <p>Use the <code>[NE]</code> flag on the | |
| <code>RewriteRule</code>. NE stands for No Escape. | |
| </p> | |
| </dd> | |
| <dt>Discussion:</dt> | |
| <dd>This technique will of course also work with other | |
| special characters that <module>mod_rewrite</module>, by default, URL-encodes.</dd> | |
| </dl> | |
| </section> | |
| <section id="time-dependent"> | |
| <title>Time-Dependent Rewriting</title> | |
| <dl> | |
| <dt>Description:</dt> | |
| <dd> | |
| <p>We wish to use <module>mod_rewrite</module> to serve different content based on | |
| the time of day.</p> | |
| </dd> | |
| <dt>Solution:</dt> | |
| <dd> | |
| <p>There are a lot of variables named <code>TIME_xxx</code> | |
| for rewrite conditions. In conjunction with the special | |
| lexicographic comparison patterns <code><STRING</code>, | |
| <code>>STRING</code> and <code>=STRING</code> we can | |
| do time-dependent redirects:</p> | |
| <highlight language="config"> | |
| RewriteEngine on | |
| RewriteCond "%{TIME_HOUR}%{TIME_MIN}" >0700 | |
| RewriteCond "%{TIME_HOUR}%{TIME_MIN}" <1900 | |
| RewriteRule "^foo\.html$" "foo.day.html" [L] | |
| RewriteRule "^foo\.html$" "foo.night.html" | |
| </highlight> | |
| <p>This provides the content of <code>foo.day.html</code> | |
| under the URL <code>foo.html</code> from | |
| <code>07:01-18:59</code> and at the remaining time the | |
| contents of <code>foo.night.html</code>.</p> | |
| <note type="warning"><module>mod_cache</module>, intermediate proxies | |
| and browsers may each cache responses and cause the either page to be | |
| shown outside of the time-window configured. | |
| <module>mod_expires</module> may be used to control this | |
| effect. You are, of course, much better off simply serving the | |
| content dynamically, and customizing it based on the time of day.</note> | |
| </dd> | |
| <dt>Discussion:</dt> | |
| <dd> | |
| <p>Serving dynamic content through your application is almost | |
| always a better approach. Caching by browsers, proxies, and | |
| <module>mod_cache</module> makes time-based rewriting unreliable | |
| in practice.</p> | |
| </dd> | |
| </dl> | |
| </section> | |
| <section id="on-the-fly-content"> | |
| <title>On-the-fly Content-Regeneration</title> | |
| <dl> | |
| <dt>Description:</dt> | |
| <dd> | |
| <p>We wish to dynamically generate content, but store it | |
| statically once it is generated. This rule will check for the | |
| existence of the static file, and if it's not there, generate | |
| it. The static files can be removed periodically, if desired (say, | |
| via cron) and will be regenerated on demand.</p> | |
| </dd> | |
| <dt>Solution:</dt> | |
| <dd> | |
| This is done via the following ruleset: | |
| <highlight language="config"> | |
| # This example is valid in per-directory context only | |
| RewriteCond "%{REQUEST_URI}" !-U | |
| RewriteRule "^(.+)\.html$" "/regenerate_page.cgi" [PT,L] | |
| </highlight> | |
| <p>The <code>-U</code> operator determines whether the test string | |
| (in this case, <code>REQUEST_URI</code>) is a valid URL. It does | |
| this via a subrequest. In the event that this subrequest fails - | |
| that is, the requested resource doesn't exist - this rule invokes | |
| the CGI program <code>/regenerate_page.cgi</code>, which generates | |
| the requested resource and saves it into the document directory, so | |
| that the next time it is requested, a static copy can be served.</p> | |
| <p>In this way, documents that are infrequently updated can be served in | |
| static form. if documents need to be refreshed, they can be deleted | |
| from the document directory, and they will then be regenerated the | |
| next time they are requested.</p> | |
| </dd> | |
| <dt>Discussion:</dt> | |
| <dd> | |
| <p>Modern approaches such as <module>mod_cache</module>, CDN | |
| caching layers, or application-level caching provide more robust | |
| and controllable solutions for serving pre-generated static | |
| content. The <code>-U</code> subrequest test used here also | |
| carries a performance cost on every request.</p> | |
| </dd> | |
| </dl> | |
| </section> | |
| </manualpage> |