Mod Rewrite Fun and Games
Wed, 5 December, 2007 – 11:55 pm
I've been having fun and games with Mod Rewrite over the past few days. One of my clients has had an SEO go over the sites and come up with a report on improvements, one of which was mod rewrite. I've known this needed doing for ages but as I didn't know how to do it I didn't due to lack of time for learning it. However now being faced with having to, and instead of being supplied with the code, just given links to a few sites to read, I've blown my mind apart over it for the past 4 days! I'm not about to go into the ins and outs of mod rewrite as there are plenty of sites out there that explain it. However I am going to go over what took me so long, not the rewrite but the redirects on the old domains.
The Rewrite
First off, to explain what mod rewrite is. Commonly used to turn dynamic URLs into static ones, ie. get rid of the question mark and variables. For example, on this site I have a form of mod rewrite running and the URL you'll see for this page is a clean, easy to read URL where as the true one would be /blog/?p=660. Essentially, by using mod rewrite you're masking the true URL with an easier to read one. You do this by creating an internal redirect which means that you tell the server (Apache only) that the clean URL should be loading the dynamic URL, but you're not telling it to actuall redirect to it, just to load the page. Make sense? Yeah I was confused first off too.
A redirect that anyone with an inkling of Apache knowledge with know, can look like
RewriteRule ^blog/about-me/$ /blog/?page_id=3 [R=301,L]
This says, if someone goes to the address /blog/about-me/ then redirect them to /blog/?page_id=3 and make it a 301 redirect so that spiders and bots also realise it's a permanent redirect. That's an external redirect as you are making the browser request the new page (with the R=301 bit) and the address in the URL bar would become the second address. So an internal redirect is virtually the same but without the request so
RewriteRule ^blog/about-me/$ blog/?page_id=3 [L]
This would then do the same, however in the URL bar the first address would be visible. That is the rewrite done. Pretty simple and straightforward. However my question was then, what about the old URLs. Afterall, this is for SEO and the last thing you want is to have the same page served up under two different URLs. This is what has had me going round in circles for the past few days. My first attempt was an external redirect as per the first code, but the opposite (ie. making the dynamic page redirect to the static page). This caused an endless loop and looking around the web a lot of people had the same issue but no one explained in plain english how to solve it.
The Redirect
After reading over various forums I finally came across some code that didn't explain it but after hacking around I managed to get it working. The final code being
Options +FollowSymLinks
RewriteEngine On
RewriteBase /
# Internally rewrite friendly URL requests to script filepath
RewriteRule ^test/$ test.php [L]
#
# Externally redirect client requests for unfriendly URL to friendly URL
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /test\.php\ HTTP/
RewriteRule ^test\.php$ /test/? [R=301,L]
This is a simple version. The actual file is test.php however I want people to use /test/ instead. So after the first 3 lines of code, which are needed only once, you have the internal redirect saying that if the address of /test/ is loaded, run test.php. Then the last 2 lines make the redirect work so that test.php cannot be loaded via the address bar (ie. you cannot go to /test.php as it will redirect to /test/). I had the second line of code, that's pretty obvious, it was the first line, the Rewrite Condition that I couldn't work out!
To explain this line it's easier to first see how the request would look:
GET /test.php HTTP/1.1
So the above is what THE_REQUEST would hold. The next part is a bit of pattern matching. The first bit says that the string should start with a 3-9 character long string of capital letters (GET, POST etc) followed by a space (\ ) then the filename of /test.php and then HTTP/ at the end followed by anything else (note with no $ at the end, there is no end of string determined so anything could follow the HTTP/ ). This line matches that the request was made by test.php and not /test/ so if this condition is met then a 301 redirect is made to send test.php off to /test/.
Of course because of the condition you could change the last line to just be:
RewriteRule ^.*$ /test/? [R=301,L]
However it's handy to know of both options.
Query Strings
Another pointer I had to work out for myself was that on this last line, the new URL can have a question mark at the end of it (/test/?). The difference with this determines whether a query string is appended to the end or not. For example, let's say you actually had the URL of test.php?id=hello and you wanted this to go to a: /test/?id=hello and b: /test/
So first off you need to update the condition to check on it having a query string. If you don't want to be too specific then you can easily do this with the following:
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /test\.php[^/]*\ HTTP/
This means that after test.php there is an option of other characters, other than a forwardslash. This would then make the condition true for test.php or test.php?id=hello. So to get this to redirect to /test/?id=hello you would change the last line to not have a question mark at the end ie.
RewriteRule ^.*$ /test/ [R=301,L]
This would then produce the desired effect and append any query string to the end of the new URL. To do the opposite of this, just add the question mark in, and then any query string would be ignored.
Summary
The above code will actually now allow you to set up mod rewrite however I've just gone over the bits that got me stumped, afterall there's more than enough literature on the subject. The sites that got it through to me were
- Changing Dynamic URLs to Static URLs – Webmaster World
- Mod Rewrite Tips and Tricks – Search Engine Watch
- URL Rewriting – HTMLSource
Plus an understanding of Pattern Matching may be of use too.


I think I'll bookmark this post. Although you've not covered anything necessarily new for me, I have a habit of forgetting how to do bits and then putting myself in an infinite loop which makes Firefox cry
By Jem on Thu, 6 December, 2007
Jem, in part I sometimes write these posts so that I know where I can get the code from again!
By Sarah on Fri, 7 December, 2007
Hello Friend,
Is there any way If user types test.php in browser we redirect him to /test/
Please HELP ME I am newbie in mod_rewrite
Thanks
Girish
By Girish on Wed, 16 July, 2008
The code you need is in the post above?
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /test\.php[^/]*\ HTTP/RewriteRule ^.*$ /test/ [R=301,L]
By Sarah on Wed, 16 July, 2008
Hello Sarah,
Thanks for the quick reply.
I used below code in my.htacess file but still the same problem if I use http://www.agnisdesigners.com/rewrite/test.php its shows the same url in the browser but if i type http://www.agnisdesigners.com/rewrite/test/
it works.
But How do I convert my all dynamic links to static DO i need change it manually or have right some extra code. If mod is not available I can use my dynamic code.
Please guide me.
THANKS AGAIN
Options +FollowSymLinks
RewriteEngine On
RewriteBase /
# Internally rewrite friendly URL requests to script filepath
RewriteRule ^test/$ test.php [L]
#
# Externally redirect client requests for unfriendly URL to friendly URL
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /test\.php[^/]*\ HTTP/
RewriteRule ^.*$ /test/ [R=301,L]
By Girish on Wed, 16 July, 2008
Hi Girish,
Have you tried the third code block in the post. It's almost the same except doesn't have the [^/] in the code. Not that it should make a difference but that piece of code is exactly what you're after.
By Sarah on Wed, 16 July, 2008