Rewrite Rule tips from corz.org

i get tips from http://corz.org/serv/tricks/htaccess2.php about rewrite rule.

beginning rewriting..



you only need to do this once per .htaccess file:
Options +FollowSymlinks
RewriteEngine on

simple rewriting

all requests to whatever.htm will be sent to whatever.php:
Options +FollowSymlinks
RewriteEngine on
RewriteRule ^(.*)\.htm$ $1.php [NC]


this will do a "real" external redirection:
Options +FollowSymlinks
RewriteEngine on
RewriteRule ^(.+)\.htm$ http://corz.org/$1.php [R,NC]

not-so-simple rewriting ... flat links and more

a more complex rewrite rule:
Options +FollowSymlinks
RewriteEngine on
RewriteRule ^files/([^/]+)/([^/]+).zip /download.php?section=$1&file=$2 [NC]


an even more complex rewrite rule:
Options +FollowSymlinks
RewriteEngine on
RewriteRule ^blog/([0-9]+)-([a-z]+) http://corz.org/blog/index.php?archive=$1-$2 [NC]

Here's the very basics of regexp (expanded from the Apache mod_rewrite documentation)..


Escaping:

\char escape that particular char

For instance to specify special characters.. [].()\ etc.

Text:

. Any single character (on its own = the entire URI)
[chars] Character class: One of following chars
[^chars] Character class: None of following chars
text1|text2 Alternative: text1 or text2 (i.e. "or")

e.g. [^/] matches any character except /
(foo|bar)\.html matches foo.html and bar.html

Quantifiers:

? 0 or 1 of the preceding text
* 0 or N of the preceding text (hungry)
+ 1 or N of the preceding text

e.g. (.+)\.html? matches foo.htm and foo.html
(foo)?bar\.html matches bar.html and foobar.html

Grouping:

(text) Grouping of text

Either to set the borders of an alternative or
for making backreferences where the nthe group can
be used on the target of a RewriteRule with $n

e.g. ^(.*)\.html foo.php?bar=$1

Anchors:

^ Start of line anchor
$ End of line anchor

An anchor explicitly states that the character right next to it MUST
be either the very first character ("^"), or the very last character ("$")
of the URI string to match against the pattern, e.g..

^foo(.*) matches foo and foobar but not eggfoo
(.*)l$ matches fool and cool, but not foo

shortening URLs

beware the regular expression:
Options +FollowSymlinks
RewriteEngine On
RewriteRule ^grab /public/files/download/download.php

capturing variables

capturing a $_GET variable:
Options +FollowSymlinks
RewriteEngine On
RewriteCond %{QUERY_STRING} foo=(.*)
RewriteRule ^grab(.*) /page.php?bar=%1


QSA Overkill!:
Options +FollowSymlinks
RewriteEngine On
RewriteCond %{QUERY_STRING} foo=(.+)
RewriteRule ^grab/(.*) /%1/index.php?file=$1 [QSA]


mixing flat and dynamic links in a single ruleset..
Options +FollowSymlinks
RewriteEngine On
RewriteCond %{QUERY_STRING} version=(.+)
RewriteRule ^grab/([^/]+)/(.*) /%1/index.php?section=$1&file=$2 [QSA]

just a demo!
Options +FollowSymlinks
RewriteEngine On
RewriteCond %{QUERY_STRING} .
RewriteRule foo.php(.*) /foo.php? [L]

cooler access denied

they go "huh?.. ahhh!"
# send them up!
Options +FollowSymlinks
RewriteEngine on
RewriteRule ^(.*)$ ../ [NC]


they go damn! Oh!
# send them exactly there!
Options +FollowSymlinks
RewriteEngine on
RewriteRule ^(.*)$ /comms/hardware/router/ [NC]


deny with style!
# users can load only "special.zip", and the css and js files.
Options +FollowSymlinks
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !^(.+)\.css$
RewriteCond %{REQUEST_FILENAME} !^(.+)\.js$
RewriteCond %{REQUEST_FILENAME} !special.zip$
RewriteRule ^(.+)$ /chat/ [NC]

all-in-one control..
RewriteEngine on
Options +FollowSymlinks
# allow access with no restrictions to local machine at 192.168.1.3
RewriteCond %{REMOTE_ADDR} !192.168.1.3
# allow access to all .css and .js in sub-directories..
RewriteCond %{REQUEST_URI} !\.css$
RewriteCond %{REQUEST_URI} !\.js$
# allow access to the files inside img/, but not a directory listing..
RewriteCond %{REQUEST_URI} !img/(.*)\.
# allow access to these particular files...
RewriteCond %{REQUEST_URI} !comments.php$
RewriteCond %{REQUEST_URI} !corzmail.php$
RewriteCond %{REQUEST_URI} !digitrack.php$
RewriteCond %{REQUEST_URI} !gd-verify.php$
RewriteCond %{REQUEST_URI} !post-dumper.php$
RewriteCond %{REQUEST_URI} !print.php$
RewriteCond %{REQUEST_URI} !source-dump.php$
RewriteCond %{REQUEST_URI} !textview.php$
RewriteRule ^(.*)$ / [R,NC,L]

Ban User Agents, referrers, script-kiddies and more..

Who need's a local copy, when I'm right here?..
RewriteCond %{HTTP_USER_AGENT} ^Teleport\ Pro [NC]
RewriteRule . abuse.txt [L]


A little garlic for the net vampires..
RewriteCond %{HTTP_USER_AGENT} ^BackWeb [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^Bandit [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^BatchFTP [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^BecomeBot [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^BlackWidow [NC,OR]
# etc..
RewriteCond %{HTTP_USER_AGENT} ^Net\ Vampire [NC]
RewriteRule . abuse.txt [L]


Suckers, h4x0rz, kiddies, cross-site scripters and more.. Bye now!
# why not come visit me directly?
RewriteCond %{HTTP_REFERER} \.opendirviewer\. [NC,OR]
# this prevents stoopid cross-site discovery attacks..
RewriteCond %{THE_REQUEST} \?\ HTTP/ [NC,OR]
# please stop pretending to be the Googlebot..
RewriteCond %{HTTP_REFERER} users\.skynet\.be.* [NC,OR]
# really, we need a special page for these twats..
RewriteCond %{QUERY_STRING} \=\|w\| [NC,OR]
RewriteCond %{THE_REQUEST} etc/passwd [NC,OR]
RewriteCond %{REQUEST_URI} owssvr\.dll [NC,OR]
# you can probably work these out..
RewriteCond %{QUERY_STRING} \=\|w\| [NC,OR]
RewriteCond %{THE_REQUEST} \/\*\ HTTP/ [NC,OR]
# etc..
RewriteCond %{HTTP_USER_AGENT} Sucker [NC]
RewriteRule . abuse.txt [L]

prevent hot-linking

how DARE they!
Options +FollowSymlinks
# no hot-linking
RewriteEngine On
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http://(www\.)?corz\.org/ [NC]
RewriteCond %{REQUEST_URI} !hotlink\.(gif|png) [NC]
RewriteRule .*\.(gif|jpg|png)$ http://corz.org/img/hotlink.png [NC]

lose the "www"
beware the regular expression:
Options +FollowSymlinks
RewriteEngine on
RewriteCond %{http_host} ^www\.corz\.org [NC]
RewriteRule ^(.*)$ http://corz.org/$1 [R=301,NC]

here's what I'm currently using:
Options +FollowSymlinks
RewriteEngine on
RewriteCond %{HTTP_HOST} ^www\.(.*) [NC]
RewriteRule ^(.*)$ http://%1/$1 [R=301,NC,L]

multiple domains in one root
All requests NOT already rewritten into these folders, transparently rewrite..
#two domains served from one root..
RewriteCond %{HTTP_HOST} domain-one.com
RewriteCond %{REQUEST_URI} !^/one
RewriteRule ^(.*)$ one/$1 [L]

RewriteCond %{HTTP_HOST} domain-two.com
RewriteCond %{REQUEST_URI} !^two
RewriteRule ^(.*)$ two/$1 [L]

automatic translation

they simply add their country code to the end of the link, or you do..
Options +FollowSymlinks
RewriteEngine on
RewriteRule ^(.*)-fr$ http://www.google.com/translate_c?hl=fr&sl=en&u=http://corz.org/$1 [R,NC]
RewriteRule ^(.*)-de$ http://www.google.com/translate_c?hl=de&sl=en&u=http://corz.org/$1 [R,NC]
RewriteRule ^(.*)-es$ http://www.google.com/translate_c?hl=es&sl=en&u=http://corz.org/$1 [R,NC]
RewriteRule ^(.*)-it$ http://www.google.com/translate_c?hl=it&sl=en&u=http://corz.org/$1 [R,NC]
RewriteRule ^(.*)-pt$ http://www.google.com/translate_c?hl=pt&sl=en&u=http://corz.org/$1 [R,NC]


the same sort of thing, except browser-side..
javascript:void(location.href='http://translate.google.com/translate?u='+location.href)


httpd.conf
Remember, if you put these rules in the main server conf file (usually httpd.conf) rather than an .htaccess file, you'll need to use ^/... ... instead of ^... ... at the beginning of the RewriteRule line, in other words, add a slash.


inheritance..
If you are creating rules in sub-folders of your site, you need to read this.


this works fine, site-wide, in my main .htaccess file
# main (top-level) .htaccess file..
# requests to file.htm goto file.php
Options +FollowSymlinks
RewriteEngine on
RewriteRule ^(.*)\.htm$ http://corz.org/$1.php [R=301,NC]


Here's my updated /osx/.htaccess file, with the .htm >> .php redirection rule reinserted..

but I'll need to reinsert the rules for it to work in this sub-folder
# /osx/.htaccess file..
Options +FollowSymlinks
RewriteEngine on
RewriteRule some rule that I need here
RewriteRule some other rule I need here
RewriteRule ^(.*)\.htm$ http://corz.org/osx/$1.php [R=301,NC]


it's a good idea to put all your rules in your main .htaccess file..
# root /.htaccess file..
Options +FollowSymlinks
RewriteEngine on
# .htm >> .php is now be covered by our main rule, there's no need to repeat it.
# But if we do need some /osx/-specific rule, we can do something like this..
RewriteRule ^osx/(.*)\.foo$ /osx/$1.bar [R=301,NC]


handy for avatar folders, to allow hot-linking, etc..
RewriteEngine off

cookies

create a cookie called "example-cookie", and set its value to "true"..
Header set Set-Cookie "example-cookie=true"

check for that same cookie + value..
Options +FollowSymlinks
RewriteEngine on
RewriteCond %{HTTP_COOKIE} !example-cookie=true
RewriteRule .* /err/401.php
conclusion
In short, mod_rewrite enables you to send browsers from anywhere to anywhere. You can create rules based not simply on the requested URL, but also on such things as IP address, browser agent (send old browsers to different pages, for instance), and even the time of day; the possibilities are practically limitless.

The ins-and outs of mod_rewrite syntax are topic for a much longer document than this, and if you fancy experimenting with more advanced rewriting rules, I urge you to check out the Apache documentation.

If you have Apache installed on your system, there will likely be a copy of the Apache manual, right here, and the excellent mod_rewriting guide, lives right here. do check out the URL Rewriting Engine notes for the juicy syntax bits. That's where I got the cute quote for the top of the page, too.

;o) Cor

troubleshooting tips..
Fatal Redirection
If you start messing around with 301 redirects [R=301], aka. "Permanently Redirected", and your rule isn't working, you could give yourself some serious headaches..

Once the browser has been redirected permanently to the wrong address, if you then go on to alter the wonky rule, your browser will still be redirected to the old address (because it's a browser thing), and you may even go on to fix, and then break the rule all over again without ever knowing it. Changes to 301 redirects can take a long time to show up in your browser.

Solution: restart your browser, or use a different one.

Better Solution: Use [R] instead of [R=301] while you are testing . When you are 100% certain the rule does exactly as it's expected to, then switch it to [R=301] for your live site.

rewrite logging..
When things aren't working, you may want to enable rewrite logging. I'll assume you are testing these mod_rewrite directives on your development mirror, or similar setup, and can access the main httpd.conf file. If not, why not? Testing mod_rewrite rules on your live domain isn't exactly ideal, is it? Anyway, put this somewhere at the foot of your http.conf..

Expect large log files..
#
# ONLY FOR TESTING REWRITE RULES!!!!!
#
RewriteLog "/tmp/rewrite.log"
#RewriteLogLevel 9
RewriteLogLevel 5


Set the file location and logging level to suit your own requirements. If your rule is causing your Apache to loop, load the page, immediately hit your browser's "STOP" button, and then restart Apache. All within a couple of seconds. Your rewrite log will be full of all your diagnostic information, and your server will carry on as before.

Setting a value of 1 gets you almost no information, setting the log level to 9 gets you GIGABYTES! So you must remember to comment out these rules and restart Apache when you are finished because, not only will rewrite logging create space-eating files, it will seriously impact your web server's performance.

RewriteLogLevel 5 is very useful, but 2 is probably enough information for most issues.



debug-report.php
A php script to make your mod_rewrite life easier!
When things aren't working as you would expect, rewrite logging is a good option, but on a hosted server, you probably won't have that option, without access to httpd.conf. Fortunately, what's usually required is no more than a quick readout of all the current server variables, $_GET array, and so on; so you can see exactly what happened to the request.

For another purpose, I long ago created debug.php, and later, finding all this information useful in chasing down wonky rewrites, created a "report" version, which rather than output to a file, spits the information straight back into your browser, as well as $_POST, $_SESSION, and $_SERVER arrays, special variables, like __FILE__, and much more.

Usage is simple; you make it your target page, so in a rule like this..

RewriteRule ^(.*)\.html$ /catch-all.php?var=$1


You would have a copy of debug-report.php temporarily renamed to catch-all.php in the root of your server, and type http://testdomain.org/foobar.html into your address bar and, with yer mojo working, debug-report.php leaps into your browser with a shit-load of exactly the sort of information you need to figure out all this stuff. When I'm messing with mod_rewrite, debug-report.php saves me time, a lot. It's way faster than enabling rewrite logging, too. Also, it's free..

Comments