mod_rewrite Flags
mod_rewrite uses “flags” to give our rewrite conditions and rules additional features. We add flags at the end of a condition or rule using square brackets, and separate multiple flags with a comma. The main flags with which you’ll need to be familiar are:
last|L
– The Last flag tells Apache to terminate a series of rewrite conditions and rewrite rules in the same way that}
will terminate a statement block in PHP. Note that this flag does not terminate mod_rewrite processing!nocase|NC
– The No Case flag tells Apache to ignore the case of the string in the regex and is often necessary when writing aRewriteCond
statement that matches the{HTTP_HOST}
variable for a domain name, which is not case sensitive.redirect|R
– The Redirect flag is used to trigger an external (visible) redirection. By default, this means that Apache will issue an HTTP 302 response to indicate that the document has been moved temporarily, but you can specify the HTTP code if you like. For example, you could use[R=301]
to make Apache issue a HTTP 301 response (moved permanently), which is often useful if you need search engines to reindex a changed URI.qsappend|QSA
– The Query String Appended flag is used to “pass through” existing query strings. You can also define a new query sting to which the old string will be appended, just be careful not to replicate key names. Failure to use theQSA
flag will cause the creation of a query string during a redirection to destroy an existing query string.forbidden|F
– The Forbidden flag is used to tell Apache when not to provide a page in response to a request. As a result, Apache will issue a HTTP 403 response, which can be used to protect files from being viewed by unauthorized visitors, bandwidth leeches, and so on.ornext|OR
– The OR flag allows you to combine rewrite conditions with a logical OR relationship as opposed to the default AND.next|N
– The Next flag tells mod_rewrite to restart the rewriting process from the first rule, but to use the URI that was returned from the last processed rewrite rule. This is useful for replacing characters in the{REQUEST_URI}
when you don’t know how many there will be.
You can read about the other available flags at Apache.org’s mod_rewrite documentation page.
mod_rewrite Comments
The RewriteEngine on
statement is needed at the start of any mod_rewrite code, but it is also useful in another role. As a good programmer, you know how important comments are in your code. mod_rewrite allows you to comment out an entire block of mod_rewrite code by wrapping the code in RewriteEngine off
and RewriteEngine on
statements:
RewriteEngine off
RewriteCond %{HTTP_HOST} !^www.example.com$ [NC]
RewriteRule .? http://www.example.com%{REQUEST_URI} [R=301,L]
RewriteEngine on
None of the statements above will be read by the mod_rewrite engine. RewriteEngine
statements can be very helpful when developing new mod_rewrite code — just use them as you would the /* ... */
wrapper for PHP comments.
mod_rewrite Tips
As a webmaster, it’s up to you to determine how your pages will be identified to visitors, as well as how to rewrite those URIs so that Apache can serve the appropriate content. Be sure to put some careful consideration into the creation of your new URI scheme. And don’t forget that after implementing your new URI scheme, you may have to go back over old content, updating existing links to match the new scheme.
When you design your new URI scheme, make use of unique keys whenever you can. In a previous example, I used countries, states and cities as keys — items that would be unique in a database. But as I build web sites for clients to update themselves, it’s unreasonable for me to insist that they provide unique titles for all their articles. Articles in the database are typically identified by an auto-incremented ID, which would be perfect for my friendly URI scheme. It makes your rewrite rules a lot simpler because you can map a URI atom to a query string value directly.
People often attempt to use a database to redirect from a title or other such field to a specific ID value. mod_rewrite has a RewriteMap
statement for this purpose, but you need to have access to your Apache main configuration file: httpd.conf
. Typically, you’ll only have access to this file if you own and operate the server. Instead, avoid the problem completely, and use the ID field to create your links.
Remember that spaces appear as %20
in URIs, so you’ll need to replace them in your PHP code. PHP’s str_replace
function is perfect for this task. Generally, we need to replace spaces with %20
when generating links, and convert %20
back to spaces when reading in query string values from the $_GET
array. However, when working with unique database field values that contain spaces, I prefer to use the underscore character to replace the spaces in resulting links. To do so, you can use the following PHP code:
$name = str_replace ( ' ', '_', $name );
Be careful not to break existing relative links when you implement your new URI scheme. Developers are often surprised when suddenly their CSS, JavaScript files, and images cease to work. Remember that relative links are relative to the current URI at the browser end — that’s the requested URI, not the rewritten one. Depending on your site’s configuration, you may need to use absolute URIs, or the HTML <base>
tag, for all your static resources.
13 mod_rewrite Examples
Earlier, we looked at an example that forced the inclusion of the www part of a domain name for every request. Let’s have a look at some more examples and see how useful mod_rewrite can be.
1. Forcing www for a domain while preserving subdomains
RewriteCond %{HTTP_HOST} ^([a-z.]+)?example.com$ [NC]
RewriteCond %{HTTP_HOST} !^www. [NC]
RewriteRule .? http://www.%1example.com%{REQUEST_URI} [R=301,L]
This rule captures the optional subdomain using the %1
variable, and, if it doesn’t start with www., redirects with www. prepended to the subdomain. The domain and the original {REQUEST_URI}
are appended to the result.
2. Eliminating www from a domain
RewriteCond %{HTTP_HOST} !^example.com$ [NC]
RewriteRule .? http://example.com%{REQUEST_URI} [R=301,L]
3. Getting rid of the www but preserving a subdomain
RewriteCond %{HTTP_HOST} ^www.(([a-z0-9_]+.)?example.com)$ [NC]
RewriteRule .? http://%1%{REQUEST_URI} [R=301,L]
Here, the subdomain is captured in %2
(the inner atom) but, since it’s optional and already captured in the %1
variable, all you need is the %1
for the subdomain.
4. Preventing image hotlinking
If some unscrupulous webmasters are leeching your bandwidth by linking to images from your site to post on theirs, you can use the following rule to block the requests:
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http://(www.)?example.com/ [NC]
RewriteRule .(gif|jpg|png)$ - [F]
If the {HTTP_REFERER}
value is not blank, or from your own domain (example.com), this rule will block the viewing of URIs ending in .gif
, .jpg
, or .png
using the forbidden flag, F
.
If you are upset enough at these hotlinkers, you could change the image and let visitors to the site know that you know that they’re hotlinking:
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http://(www.)?example.com/.*$ [NC]
RewriteRule .(gif|jpg|png)$ http://www.example.com/hotlinked.gif [R=301,L]
Instead of blocking the URI, the above rule rewrites it to a specific image in our domain. What appears in this image is completely up to your imagination!
You can block specific domains using:
RewriteCond %{HTTP_REFERER} !^http://(www.)?leech_site.com/ [NC]
RewriteRule .(gif|jpg|png)$ - [F,L]
This rule blocks all requests where the {HTTP_REFERER}
field is set to the bad domain.
Of course, the above rules rely on the {HTTP_REFERER}
value being set correctly. It usually is, but if you’d rather rely on the IP Address, use {REMOTE_ADDR}
instead.
5. Redirecting to a 404 page if the directory and file do not exist
If your host doesn’t provide for a “file not found” redirection, create it yourself!
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule .? /404.php [L]
Here, -f
matches an existing filename and -d
matches an existing directory name. This script checks to see that the requested filename is not an existing filename or directory name before it redirects to the 404.php
script. You can extend this script: include the URI in a query string by adding ?url=$1
immediately after the URI:
RewriteRule ^/?(.*)$ /404.php?url=$1 [L]
This way, your 404.php
script can do something with the requested URL: display it in a message, send it in an email alert, perform a search, and so on.
6. Renaming your directories
If you’ve shifted files around on your site, changing directory names, try this:
RewriteRule ^/?old_directory/([a-z/.]+)$ new_directory/$1 [R=301,L]
I’ve included the literal dot character (not the “any character” metacharacter) inside the set to allow file extensions.
7. Converting old .html
links to new .php
links
Updating your web site but need to be sure that bookmarked links will still work?
RewriteRule ^/?([a-z/]+).html$ $1.php [L]
This is not a redirection, so it will be invisible to your visitors. To make it permanent (and visible), change the flag to [R=301,L]
.
8. Creating extensionless links
If your site uses PHP files, and you want to make your links easier to remember — or you just want to hide the file extension, try this:
RewriteRule ^/?([a-z]+)$ $1.php [L]
If you have a mixture of both .html
and .php
files, you can use RewriteCond
statements to check whether the filename with either extension exists as a file:
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^/?([a-zA-Z0-9]+)$ $1.php [L]
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^/?([a-zA-Z0-9]+)$ $1.html [L]
If the file name exists with the .php
extension, that rule will be chosen.
9. Checking for a key in a query string
If you need to have a specific key’s value in your query string, you can check for its existence with a RewriteCond
statement:
RewriteCond %{QUERY_STRING} !uniquekey=
RewriteRule ^/?script_that_requires_uniquekey.php$ other_script.php [QSA,L]
The above code will check the {QUERY_STRING}
variable for a lack of the key uniquekey
and, if the {REQUEST_URI}
is the script_that_requires_uniquekey
, it will redirect to an alternative URI.
10. Deleting the query string
Apache’s mod_rewrite automatically passes through a query string unless you do either of the following:
- Assign a new query string (you can keep the original query string by adding a QSA flag, e.g.,
[QSA,L]
). - Add a
?
after a filename (for example,index.php?
). The?
will not be shown in the browser’s location field.
11. Redirecting a working URI to a new format
Here’s a curly one. Let’s say, for example, that we’ve got a set of working URLs that look like this: /index.php?id=nnnn
. However, we’d really like to change them to /nnnn
and make sure search engines update their indexes to the new URI format. First, we’d have to redirect the old URIs to the new ones so that search engines update their indexes, but we’d still have to rewrite the new URI back to the old one so that the index.php
script would run. Have I got your head spinning?
The trick here is to place into the query string a marker code that will not be seen by visitors. We redirect from the old link to the new format only if the “marker” is not present in the query string. Then we rewrite the new format link back to the old format, and add a marker to the query string, using the QSA flag to ensure we’re not eliminating an existing query string. Here’s how it’s done:
RewriteCond %{QUERY_STRING} !marker
RewriteCond %{QUERY_STRING} id=([-a-zA-Z0-9_+]+)
RewriteRule ^/?index.php$ %1? [R=301,L]
RewriteRule ^/?([-a-zA-Z0-9_+]+)$ index.php?marker&id=$1 [L]
Here, the original URI, http://www.example.com/index.php?id=nnnn
, does not contain the marker, so it’s redirected by the first rule to http://www.example.com/nnnn
with a HTTP 301 response. The second rule rewrites http://www.example.com/nnnn
back to http://www.example.com/index.php?marker&id=nnnn
, adding marker
and id=nnnn
in a new query string; then, the mod_rewrite process is started over.
In the second iteration, the marker is matched so the first rule is ignored and, since there’s a dot character in index.php?marker&id=nnnn
, the second rule is also ignored … and we’re finished!
Note that, while useful, this solution does require additional processing by Apache, so be careful if you’re using it on shared servers with a lot of traffic.
12. Ensuring that a secure server is used
Apache can determine whether you’re using a secure server in two ways: using the {HTTPS}
, or {SERVER_PORT}
, variables:
RewriteCond %{REQUEST_URI} ^secure_page.php$
RewriteCond %{HTTPS} !on
RewriteRule ^/?(secure_page.php)$ https://www.example.com/$1 [R=301,L]
The above example tests that the {REQUEST_URI}
value is equal to our secure page script, and that the {HTTPS}
value is not equal to on. If both these conditions re met, the request is redirected to the secure server URI. Alternatively, you could do the same thing by testing the {server_port}
value, where 443 is typically the secure server port:
RewriteCond %{REQUEST_URI} ^secure_page.php$
RewriteCond %{SERVER_PORT} !^443$
RewriteRule ^/?(secure_page.php)$ https://www.example.com/$1 [R=301,L]
13. Enforcing secure server only on selected pages
In situations where secure and unsecured domains share the web server’s DocumentRoot
directory, you’ll need a RewriteCond
statement to check that the secure server port isn’t being used, and then only redirect the request if the requested script is one in the list of those that require a secure server:
RewriteCond %{SERVER_PORT} !^443$
RewriteRule ^/?(page1|page2|page3|page4|page5)$ https://www.example.com/%1 [R=301,L]
Here’s how you’d redirect requests for pages not requiring a secure server back to port 80:
RewriteCond %{ SERVER_PORT } ^443$
RewriteRule !^/?(page6|page7|page8|page9)$ http://www.example.com%{REQUEST_URI} [R=301,L]
Summary
Apache mod_rewrite is primarily used to allow SEO and user friendly URIs, but it’s also an extremely flexible tool for other important redirection tasks. If you want to learn more, here are some very useful resources I’ve found:
Regular Expressions
- Great tutorial: http://gnosis.cx/publish/programming/regular_expressions.html
- Cheat sheet: http://regexlib.com/CheatSheet.aspx
- A regex-capable text editor: http://www.editpadpro.com
- Regex Coach: http://weitz.de/regex-coach/
Frequently Asked Questions (FAQs) about Apache Mod_Rewrite
What is the purpose of Apache Mod_Rewrite?
Apache Mod_Rewrite is a powerful and versatile Apache module that provides URL manipulation capability. It uses a rule-based rewriting engine to rewrite requested URLs on the fly. The main purpose of this module is to provide the ability to rewrite URLs in a cleaner manner, translating human-readable paths into code-friendly query strings. This can improve the usability and accessibility of a website or web application.
How does Apache Mod_Rewrite work?
Apache Mod_Rewrite works by using a rule-based rewriting engine. It checks each requested URL for patterns defined by a set of rules and then replaces the URL with a different one based on these rules. The rules are defined in the .htaccess file, which is a configuration file for use on web servers running the Apache Web Server software.
How can I enable Apache Mod_Rewrite?
To enable Apache Mod_Rewrite, you need to have administrative access to your server. First, you need to ensure that the mod_rewrite module is installed on your server. Then, you need to enable it in your Apache configuration file (httpd.conf or apache2.conf). After enabling the module, you need to restart your Apache server for the changes to take effect.
How can I use Apache Mod_Rewrite to redirect URLs?
To redirect URLs using Apache Mod_Rewrite, you need to define rewrite rules in your .htaccess file. These rules specify the pattern to match in the requested URL and the replacement URL. The syntax for a rewrite rule is: RewriteRule pattern target [flags]. The pattern is a regular expression that matches the requested URL, the target is the replacement URL, and the flags are optional parameters that change the behavior of the rule.
What are some common uses of Apache Mod_Rewrite?
Apache Mod_Rewrite is commonly used for URL redirection and URL shortening. It can also be used to prevent hotlinking, implement domain forwarding, force SSL, and block specific IP addresses or referrers. Additionally, it can be used to serve custom error pages and to map different parts of your website to different application servers.
How can I debug Apache Mod_Rewrite rules?
Debugging Apache Mod_Rewrite rules can be done by enabling the rewrite log and setting the rewrite log level. The rewrite log records all rewriting actions performed by mod_rewrite, and the rewrite log level determines the amount of detail in the log. The higher the log level, the more detail is recorded.
What are the performance implications of using Apache Mod_Rewrite?
While Apache Mod_Rewrite is a powerful tool, it can have performance implications if not used correctly. Each rewrite rule requires processing power to evaluate, so having a large number of complex rules can slow down your server. Therefore, it’s important to keep your rules as simple and efficient as possible.
Can I use Apache Mod_Rewrite with other web servers?
Apache Mod_Rewrite is specific to the Apache Web Server. However, other web servers have similar modules or features. For example, the Nginx web server has a rewrite module that provides similar functionality.
What are some common mistakes when using Apache Mod_Rewrite?
Some common mistakes when using Apache Mod_Rewrite include not enabling the mod_rewrite module, not restarting the Apache server after making changes, using incorrect syntax in rewrite rules, and not properly escaping special characters in patterns and substitutions.
Where can I find more information about Apache Mod_Rewrite?
The official Apache documentation is the best source of information about Apache Mod_Rewrite. It provides a comprehensive guide to the module, including its features, usage, and configuration. Additionally, there are many online tutorials and forums where you can find examples and get help from the community.
DK Lynn is a former instructor pilot and "rocket scientist" now living in New Zealand where he operates a small business developing and hosting web sites.