Using mod_rewrite for Backward Compatibility

Wednesday, January 14, 2004

Problem:

You just upgraded your web site to a new back-end system. The old system used URLs like this:

http://www.domain.com/cgi-bin/product.cgi?product=abc123

Your new system, however, creates “friendly” URLs, like so:

http://www.domain.com/product.php/product_model/abc123

Now, for the entire time you used the old system, Google was your friend. Your URLs may have been unfriendly, but they weren’t bad enough to deter Google. All those old links to your products are still out there, and you need some way to point them to their new locations.


Solution:

mod_rewrite

The problem above is exactly what I faced with the redesign of Crafty Goat. It took quite a bit of digging to pull together all the information I needed to solve this, so I thought I should document the answer here.

What I needed was the ability to take the product ID from the old URL, put it into the new URL, and serve a HTTP 301 (permanent redirect) response.

My early attempts looked something like this:

RewriteRule ^cgi-bin/product.cgi?product=(.*) http://www.domain.com/product.php/product_model/$1 [R,L]

This does not work for a variety of reasons. First, RewriteRule does not match against the query string (everything after the question mark). Second, this only serves up a temporary redirect (HTTP 302 response). What I needed was a RewriteCond, which can match against the query string, and a specific redirect response. Like so:

RewriteCond %{QUERY_STRING} ^product=(.*)$<br />RewriteRule ^cgi-bin/product.cgi http://www.domain.com/product.php/product_model/$1 [R=301,L]

This works, except for one thing. Apache will kindly append the old query string to the end of the new URL, just in case you need it. I do not. Luckily, this is addressed in the documentation: Simply add a question mark to the end of the new URL to eliminate the old query string. Which leaves us with this:

RewriteCond %{QUERY_STRING} ^product=(.*)$<br />RewriteRule ^cgi-bin/product.cgi http://www.domain.com/product.php/product_model/%1? [R=301,L]

Which solves our problem perfectly. Like I said before, most of what the Apache server does is complete magic to me. Hopefully, I can find time soon to learn more about this stuff.