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 subdomainsRewriteCond %{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 domainRewriteCond %{HTTP_HOST} !^example\.com$ [NC]
RewriteRule .? http://example.com%{REQUEST_URI} [R=301,L]
3. Getting rid of the www but preserving a subdomainRewriteCond %{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 hotlinkingIf 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 existIf 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 directoriesIf 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 linksUpdating 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 linksIf 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 stringIf 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 stringApache'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 formatHere'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 usedApache 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 pagesIn 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]
Cheer,