Search code examples
mod-rewriteurl-rewritinghttp-status-code-301seo

Apache mod_rewrite friendly URLs with corresponding 301 redirects


The Problem:

Been spinning my wheels and reading up on this one for awhile and looking for some help now. I'm looking to take a group of non-friendly URLs (there are actually more "groups" but this should me for an example):

  1. domainname.com/?section=zebras
  2. domainname.com/?section=monkeys&id=555

and turn them into friendly URLs, as well as do a 301 on the old versions, so that any old bookmarks (and search engines) will still resolve them. The new format I'm looking for would be:

  1. domainname.com/zebras/
  2. domainname.com/monkeys/555

I'm fully intending to write separate RewriteCond/RewriteRule combinations for each of those scenarios, so I don't necessarily need a super-rule that catches all my scenarios. Oh and this is all in .htaccess.

My Progress:

I was originally getting into a redirect loop because I was just doing two RewriteRules back to back - one for the friendly URL and one for the 301 redirect. Came across my favorite way (so far) around the redirect loop which works (for my scenario #1 at least):

RewriteCond %{ENV:REDIRECT_STATUS} !200
RewriteCond %{QUERY_STRING} ^section=zebras$ [NC]
RewriteRule ^.*$ http://www.domainname.com/zebras/? [R=301,NC,L]

RewriteRule ^zebras/$ /index\.php?section=zebras [NC,L]

However, I'd like to have something that works for more than just "zebras" (for instance, I'd like it to work for "lions" as well), so I'm trying to make that more generic. What I am trying now looks like this:

RewriteCond %{ENV:REDIRECT_STATUS} !200
RewriteCond %{QUERY_STRING} ^section=([a-z]+)$ [NC]
RewriteRule ^section=([a-z]+)$ http://www.domainname.com/$1/? [R=301,NC,L]

RewriteRule ^([a-z]+)/$ /index\.php?section=$1 [NC,L]

However, this doesn't work. I think I have something "not quite right", I just can't tell what it is - there's something I'm missing or formatting incorrectly somewhere. Sorry in advance for the lengthy description, just wanted to be clear.


Solution

  • Do this:

    RewriteEngine on
    RewriteBase /
    
    RewriteCond %{ENV:REDIRECT_STATUS} 200
    RewriteRule ^ - [L]
    
    RewriteCond %{REQUEST_FILENAME} !-f 
    RewriteCond %{REQUEST_URI} !(\.\w+|/)$ 
    RewriteRule (.*) /$1/ [R,L]
    
    RewriteCond %{QUERY_STRING} ^section=([a-z]+)$ [NC]
    RewriteRule ^ /%1/? [R=301,NC,L]
    
    RewriteRule ^([a-z]+)/$ /index\.php?section=$1 [NC,L]
    
    RewriteCond %{QUERY_STRING} ^section=([a-z]+)&id=(\d+)$ [NC]
    RewriteRule ^ /%1/%2/? [R=302,NC,L]
    
    RewriteRule ^([a-z]+)/(\d+)/$ /index\.php?section=$1&id=$2 [NC,L]
    

    Description

    Prevents looping:

    RewriteCond %{ENV:REDIRECT_STATUS} 200
    RewriteRule ^ - [L]
    

    Prevents trailing slash problem:

    RewriteCond %{REQUEST_FILENAME} !-f 
    RewriteCond %{REQUEST_URI} !(\.\w+|/)$ 
    RewriteRule (.*) /$1/ [R,L]
    

    Handles rewrites with only section=([a-z]+) in them:

    RewriteCond %{QUERY_STRING} ^section=([a-z]+)$ [NC]
    RewriteRule ^ /%1/? [R=302,NC,L]
    
    RewriteRule ^([a-z]+)/$ /index\.php?section=$1 [NC,L]
    

    Handles rewrites with only section=([a-z]+)&id=(\d+) in them:

    RewriteCond %{QUERY_STRING} ^section=([a-z]+)&id=(\d+)$ [NC]
    RewriteRule ^ /%1/%2/? [R=302,NC,L]
    
    RewriteRule ^([a-z]+)/(\d+)/$ /index\.php?section=$1&id=$2 [NC,L]
    

    mistake in your rules:

    section=([a-z]+) is not available in the URI part. So, RewriteRule ^section=([a-z]+)$ never matched.