Web Application Developer.


Mod_rewrite: Attempting to bend RewriteMap, RewriteCond, and RewriteRule to my will...

Mod_rewrite is a poorly documented, yet incredibly flexible package for Apache.

I've recently been occupied with how to set up a system of "short" urls for a website, that can be easily edited by regular users. Obviously, we can't use simple redirects or even rewriterules in httpd.conf because we don't want to give everyone write access to that file.

So it looks like the most straightforward way to do it is to use a rewritemap...basically a two-column text file (source/destination) that mod_rewrite uses to "look up" an incoming url and rewrite it to our target url.

RewriteEngine On
RewriteMap redirects txt:/home/.../.../rewriterules.map
RewriteRule ^(.*)$ ${redirects:$1|$1} [R,L,NC]

How does it work? (See here for details.)

The first line turns on mod_rewrite. The next line sets the location of the map file (the two column list of source/destinations). The third line sets a RewriteRule that says "take everything in the request uri" (.*), and search the RewriteMap redirects for a match. If we have a match, the user's browser gets a redirect and is forwarded to the new uri. If we don't get a match, i've specified with the $1 backreference that the user is to be transparently passed on to the uri he requested.

The problem is that when Apache doesn't find a match in the rewritemap, we get a new request for the same uri...which runs through the process, ending in a request for the same uri...and so on. Because I want to redirect the user instead of handling the change transparently, I keep running into an infinite loop problem.

So we use a RewriteCond.

RewriteEngine On
RewriteMap redirects txt:/home/.../.../rewriterules.map
RewriteCond ${redirects:$1}   >"" [NC]
RewriteRule ^(.*)$ ${redirects:$1|$1} [R,L,NC]

We basically say "Look up the uri from the following RewriteRule ($1 means look ahead to the next RewriteRule. We could look behind to the last RewriteCond by using %1.) and if it is lexically greater than the empty string then do the Rewrite. Otherwise, skip it.

Version 2

I never got the [NC] switches to work so I just converted everything to lowercase before I do the comparisons (You must make sure your map is all lowercase).

RewriteEngine On
RewriteMap redirects txt:/home/.../.../rewriterules.map
RewriteMap lowercase int:tolower
RewriteCond ${lowercase:%{REQUEST_URI}|NONE} ^(.+)$
RewriteCond ${redirects:%1}   >"" 
RewriteRule ^(.*)$ ${redirects:%1} [R,L]

Links



Posted by Jeremy Tunnell on August 21st 9:29 PM

Tags




 

Comments


Thanks for this tip, was having a problem with an infinite loop on a rewrite map - this helped.

Posted by James on September 14th 09:45:52 AM



Found this via Google. Thanks for the tip!

Posted by foo on February 10th 01:25:34 PM



Thank you for sharing this missing part of the Apache documentation. Worked great for me.

Posted by Fredrik Nygren on March 17th 09:22:07 AM



Thank you very much

Posted by sohbet on December 13th 07:21:04 PM



thx for this great article, i searched for hours!

Posted by Felix on January 23rd 06:46:22 AM



Thanks for this article, solved a little typo in my script and only noticed it after looking at this article.

Posted by Web Design Sydney Melbourne Brisbane on March 17th 01:25:47 AM



Oh, can I kiss your toes?

Seriously, I didn't grok the bit about using rewritemaps in a RewriteCond before. Thanks!

Peter

Posted by Peter Burkholder on July 14th 12:51:36 PM



Thanks for this!

Just one thing - the syntax for the "greater than the empty string" is wrong - >"" fails to match certain things (for example, if the result of your rewritemap is the number 9). The correct syntax is "> " (note the order of the quotes), which matches any ascii value lexically greater than space (char(32)).

Posted by Andy Theyers on July 20th 01:35:54 PM



Here's a little solution I came up with for making my URLs prettier and getting search engines to repoint to the new URL. I have a products db that has uppercase word keys for each product that points to the numeric product id. Then I have a reverse db that contains the same in the opposite order. So far this seems to work..


RewriteMap uppercase int:toupper
RewriteMap products dbm:conf/products.dbm
RewriteMap rproducts dbm:conf/rproducts.dbm

# 301 redirect ugly product URL to user-friendly word based URL.
RewriteCond %{QUERY_STRING} ^partNumber=(\d*)$
RewriteCond ${rproducts:%1} >""
RewriteRule ^/product\.html$ /product/${rproducts:%1|%1}? [R=301,L]

# Convert user-friendly word based product URL internally to ugly URL with part number.
RewriteRule ^/product/(\D.*)$ /product.html?partNumber=${products:${uppercase:$1}} [E=PAGE:product.html]

Posted by MikeFM on July 28th 09:54:11 PM



Hi, I am working on the rewritemap and rewriterule for my website. actually requirement is like i have one website whose address is http://www.myside1.com now since i am going to retire this website so i need to redirect most of the pages to new website. i want to use map file for this. i have created this mapping file where i want to keep maping like /folder1/page1.html http://newwebsite.com/content/view/hello.html in this way i want to use the redirection. i dont want to hardcode the each redirect rule. I can have /folder2/page1.html also. Could you please help me to have the correct rule.

Posted by yashjimmy on July 09th 12:15:54 PM


Add a Comment