Forum Moderators: phranque

Message Too Old, No Replies

mod rewrite expert advice

Request for a mod_rewrite expert to give a newbie's code a once-over

         

fishwebby

11:29 am on Nov 15, 2007 (gmt 0)

10+ Year Member



Hello,

as someone relatively new to the delights of mod_rewrite, I would really appreciate it if someone more experienced with this could take a quick look at my code. As far as my tests show it all works, but as it's doing several things (compiled from different examples I found) I want to make sure it's not doing any thing superfluous/there are no obvious redundant bits/there are any glaring bugs etc.

I want to do basically 4 things:

  • Remove the leading "www" from all requests (if present)
  • Add a trailing slash to the end (if not present, before the querystring if there is one)
  • Redirect all requests to an index page in the root - it's a multilingual website and I have one index page per language (e.g. index.en.php in English). Later I load the relevant content by checking $_SERVER['REQUEST_URI'] in PHP
  • If someone requests the root and a "lang" cookie is set, redirect to that subdirectory, otherwise do nothing (the first index page is a page where you choose the language, which is later remembered in a cookie). E.g. http://example.com/ -> http://example.com/en/

The code is below (I've got it in a .htaccess file).


RewriteEngine On
# Remove leading www from all requests
RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
RewriteRule ^(.*)$ http://example.com/$1 [R=301,L]
# Add trailing slash if not present
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !(.*)/$
RewriteRule ^(.*)$ http://example.com/$1/ [L,R=301]
# Redirect to relevant index page in the root
RewriteRule ^(enŠfrŠes) index.$1.html [NC,QSA]
# Redirect to language subsite if cookie set
RewriteCond %{HTTP_cookie} ^lang=(enŠeuŠes)$
RewriteRule ^$ http://example.com/%1/

Any comments/criticisms/suggestions for improvements are greatly appreciated!

[edited by: jdMorgan at 2:23 pm (utc) on Nov. 15, 2007]
[edit reason] example.com [/edit]

jdMorgan

4:25 pm on Nov 15, 2007 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



I replied to this once already, but must not have submitted it properly. I'd suggest the following improvements and clarifications:

RewriteEngine On
RewriteBase /
#
# Externally redirect to add missing trailing slash to non-file URLs
RewriteCond %{REQUEST_URI} !/$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule (.*) http://example.com/$1/ [R=301,L]
#
# Externally redirect to language subsite if cookie set
RewriteCond %{HTTP_COOKIE} ^lang=(enŠeuŠes)$
RewriteRule ^$ http://example.com/%1/ [R=302,L]
#
# Externally redirect to remove leading www from all requests (if not already done by rules above)
RewriteCond %{HTTP_HOST} ^www\.example\.com [NC]
RewriteRule (.*) http://example.com/$1 [R=301,L]
#
# Internally rewrite to relevant index page in the root
RewriteRule ^(enŠfrŠes)/ index.$1.html [NC,L]

Place the most-specific external redirects first, then less-specific redirects, most-specific internal rewrites, then less-specific. This prevents two back-to-back redirects if, for example, www.example.com/ is requested with the lang cookie set. Doing the external redirects before the internal rewrites prevents the internally-rewritten URL-paths from being exposed to the client.

The other important change was to the RewriteCond order in the now-first rule: It is important to avoid the slow and expensive 'file-exists' check unless it is really necessary, so always place these last when using multiple RewriteConds. The other even-slower RewriteCond is doing a %{REMOTE_HOST} check, which requires your server to issue a reverse-DNS lookup request to a (usually-external) DNS server. File and directory exists checks and reverse-DNS checks should therefore be avoided or put off 'til last based on all the other RewriteConds matching.

Other changes were made to eliminate unnecessary, incorrect, and/or redundant regex tokens, and to make the comments more accurate.

I note that you've got (enŠeuŠes) in one rule, and (enŠfrŠes) in the other. These should most likely be consistent.

Change all broken pipe "Š" characters above to solid pipes before use; Posting on this forum modifies the pipe characters.

Jim

fishwebby

7:58 pm on Nov 15, 2007 (gmt 0)

10+ Year Member



Hi Jim,

thank you very much for your fantastic reply! I've learnt a lot from that.

It's interesting to know about the domain "example.com" too - I didn't know that was a domain name reserved specifically for example code.

Thanks again!

Best wishes,
Dave

fishwebby

9:01 pm on Nov 15, 2007 (gmt 0)

10+ Year Member



Hello again,

I was just testing the script when I noticed something - if there is only the one cookie, then it works fine. However, if there is more than one (the PHP session id for example), then it doesn't redirect to the language subsite. I managed to work out that this was due to the regular expression not matching "lang=en" because the HTTP_COOKIE value contains all the cookie values, each separated by a semicolon and a space. To get this to work, I've added the bits in bold to this line:

RewriteCond %{HTTP_COOKIE} ^(.*;\ )?(lang=(enŠeuŠes))(;\ .*)?$

And I had to change the back-reference to "%3" to get the cookie value in this line:

RewriteRule ^$ http://example.com/%3/ [R=302,L]

And it's all fine and dandy now. :-)

jdMorgan

2:05 am on Nov 16, 2007 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member




RewriteCond %{HTTP_COOKIE} lang=(enŠeuŠes)

is entirely equivalent, and *much* simpler -- Use with %1 as before.

Jim

[edited by: jdMorgan at 2:05 am (utc) on Nov. 16, 2007]

fishwebby

6:59 am on Nov 16, 2007 (gmt 0)

10+ Year Member



I just tested it again, and if I restart the browser it (your way) works. I'm thinking the problem I was having when testing it first time round is that the browser was doing something odd with cacheing - it just wouldn't redirect at all if there were two cookies. When I made the changes I put above it worked. However, I'm now happy that it works your original way and I've changed it back. :-)

Thanks again for all the help,

Best wishes,
Dave