Complicated rewrite

Oh, the strange things mod_rewrite does!

Complicated rewrite

Postby pnoeric » Wed Apr 26, 2006 10:46 am

Hi, I'm doing two rewrites, one that should be "visible" to the customer, and one that's internal.

If the customer comes to:

http://site.com/catalog/product.php?itemNo=1

Then I want to rewrite it (with a 301 redirect so it's visible to the customer and search engines) to
http://site.com/catalog/product/1/

AND... then.... if the user comes to
http://site.com/catalog/product/1/

then invisibly ;-) I want to internally just rewrite that back to
http://site.com/catalog/product.php?itemNo=1

And have it go on through.

I have this done as I think it should work, but it's failing on the 2nd part, the internal rewrite back to the .php script.

Here's my rules:

Code: Select all
# rewrite old style to new style, make rewrite visible
# note '?' at end of rule, to dump the arguments from the end of the url
RewriteCond %{QUERY_STRING} itemNo=([A-Za-z0-9]+)
RewriteRule ^catalog/product.php /catalog/product/%1/? [R=301,L]

# now rewrite new style to old style, internally (do not show user)
RewriteRule ^catalog/product/([A-Za-z0-9]+)$ /catalog/product.php?itemNo=$1 [L]
RewriteRule ^catalog/product/([A-Za-z0-9]+)/$ /catalog/product.php?itemNo=$1 [L]


Here's the output from my log if that helps:

/initial] (3) [per-dir /usr/local/apache2/htdocs/] add path info postfix: /usr/local/apache2/htdocs/catalog/product -> /usr/local/apache2/htdocs/catalog/product/108/
/initial] (3) [per-dir /usr/local/apache2/htdocs/] strip per-dir prefix: /usr/local/apache2/htdocs/catalog/product/108/ -> catalog/product/108/
/initial] (3) [per-dir /usr/local/apache2/htdocs/] applying pattern '^catalog/product.php' to uri 'catalog/product/108/'
/initial] (3) [per-dir /usr/local/apache2/htdocs/] add path info postfix: /usr/local/apache2/htdocs/catalog/product -> /usr/local/apache2/htdocs/catalog/product/108/
/initial] (3) [per-dir /usr/local/apache2/htdocs/] strip per-dir prefix: /usr/local/apache2/htdocs/catalog/product/108/ -> catalog/product/108/
/initial] (3) [per-dir /usr/local/apache2/htdocs/] applying pattern '^catalog/product/([A-Za-z0-9]+)$' to uri 'catalog/product/108/'
/initial] (3) [per-dir /usr/local/apache2/htdocs/] add path info postfix: /usr/local/apache2/htdocs/catalog/product -> /usr/local/apache2/htdocs/catalog/product/108/
/initial] (3) [per-dir /usr/local/apache2/htdocs/] strip per-dir prefix: /usr/local/apache2/htdocs/catalog/product/108/ -> catalog/product/108/
/initial] (3) [per-dir /usr/local/apache2/htdocs/] applying pattern '^catalog/product/([A-Za-z0-9]+)/$' to uri 'catalog/product/108/'
/initial] (2) [per-dir /usr/local/apache2/htdocs/] rewrite catalog/product/108/ -> /catalog/product.php?itemNo=108
/initial] (3) split uri=/catalog/product.php?itemNo=108 -> uri=/catalog/product.php, args=itemNo=108
/initial] (1) [per-dir /usr/local/apache2/htdocs/] internal redirect with /catalog/product.php [INTERNAL REDIRECT]
/initial/redir#1] (3) [per-dir /usr/local/apache2/htdocs/] strip per-dir prefix: /usr/local/apache2/htdocs/catalog/product.php -> catalog/product.php
/initial/redir#1] (3) [per-dir /usr/local/apache2/htdocs/] applying pattern '^catalog/product.php' to uri 'catalog/product.php'
/initial/redir#1] (2) [per-dir /usr/local/apache2/htdocs/] rewrite catalog/product.php -> /catalog/product/108/?
/initial/redir#1] (3) split uri=/catalog/product/108/? -> uri=/catalog/product/108/, args=<none>
/initial/redir#1] (2) [per-dir /usr/local/apache2/htdocs/] explicitly forcing redirect with http://site.com/catalog/product/108/
/initial/redir#1] (1) [per-dir /usr/local/apache2/htdocs/] escaping http://site.com/catalog/product/108/ for redirect
/initial/redir#1] (1) [per-dir /usr/local/apache2/htdocs/] redirect to http://site.com/catalog/product/108/ [REDIRECT/301]
/initial] (3) [per-dir /usr/local/apache2/htdocs/] add path info postfix: /usr/local/apache2/htdocs/catalog/product -> /usr/local/apache2/htdocs/catalog/product/108/
/initial] (3) [per-dir /usr/local/apache2/htdocs/] strip per-dir prefix: /usr/local/apache2/htdocs/catalog/product/108/ -> catalog/product/108/
/initial] (3) [per-dir /usr/local/apache2/htdocs/] applying pattern '^catalog/product.php' to uri 'catalog/product/108/'

(etc. you can see that it's repeating...)


What the heck am I doing wrong?!! ;-)

Thanks for any help-

best
Eric
pnoeric
 
Posts: 4
Joined: Sun Apr 16, 2006 5:23 pm

Postby richardk » Wed Apr 26, 2006 11:16 am

Firstly, you should not be relying on mod_rewrite to do two changes because you don't want to edit the links in your HTML. I know you might not be, but it's a warning if you are.

The problem is that Apache starts off by matching /catalog/product/1/ and internally redirects to /catalog/product.php?itemNo=1. This is a new sub request and is treated like a normal request. It goes through mod_rewrite again, matching your 301 redirect, then it redirects and matches as it did at the start because it is a new request. (The loop.)

To stop this you need to use the %{ENV:REDIRECT_STATUUS} variable.

Code: Select all
RewriteEngine On

RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{QUERY_STRING} itemNo=([A-Za-z0-9]+)
RewriteRule ^catalog/product.php /catalog/product/%1/? [R=301,L]

RewriteRule ^catalog/product/([A-Za-z0-9]+)/?$ /catalog/product.php?itemNo=$1 [L]


(The ? after the / in the last rule makes the / optional.)
richardk
 
Posts: 8800
Joined: Wed Dec 21, 2005 7:50 am

Postby pnoeric » Wed Apr 26, 2006 2:04 pm

Can't even tell you how much I appreciate this. Never thought about the solution you provided-- and it works great.

Firstly, you should not be relying on mod_rewrite to do two changes because you don't want to edit the links in your HTML. I know you might not be, but it's a warning if you are.


Completely understood and agree-- in fact, I actually did edit all the internal links, so they're good. The whole first rewrite (to "visibly" change the old style to the new style) is strictly for SEO optimization, so any old links pointing into the site are cleanly redirected to the new, spider-friendly format (sans "?").

It's kinda crazy ;-)

I'm surprised that I looked at over a dozen mod_rewrite tutorials and nobody addressed this specific issue, though I'd imagine it's somewhat common. Thanks again for your help.

best
Eric
pnoeric
 
Posts: 4
Joined: Sun Apr 16, 2006 5:23 pm

Postby pnoeric » Tue Nov 07, 2006 4:20 pm

Hi, back with a new question. A slight variation on what I asked before... I have an existing rewrite that's working GREAT, and I need to change it slightly.

Currently if someone comes to:
http://site.com/catalog/category.php?cat=1014

It is re-written to show this in the URL bar:
http://site.com/catalog/category/1014

Which is perfect. But I need to add a second variable to be carried through to my script... SO if someone comes to:
http://site.com/catalog/category.php?ca ... newvar=ABC

It should still show it as http://site.com/catalog/category/1014 in the URL bar, but internally, keep 'newvar' and pass it to my php page.

I tried adding "QSA" to the lines with [L] below (so it said [QSA,L]), which I thought would do it, but apparently not :-(

Any tips or guidance much appreciated. Mod_rewrite is quite black magic, I've decided. Powerful, but mysterious!

Here's the key parts of my existing mod_rewrite file:

Code: Select all
# rewrite old style to new style, make rewrite visible
RewriteCond %{QUERY_STRING} itemNo=([A-Za-z0-9]+)
RewriteRule ^/catalog/product.php /catalog/product/%1/? [R=301,L]

# now rewrite new style to old style, make it invisible

RewriteRule ^/catalog/category/([A-Za-z0-9]+)$ /catalog/category.php?cat=$1 [L]
RewriteRule ^/catalog/category/([A-Za-z0-9]+)/$ /catalog/category.php?cat=$1 [L]

RewriteRule ^/catalog/product/([A-Za-z0-9]+)$ /catalog/product.php?itemNo=$1 [L]
RewriteRule ^/catalog/product/([A-Za-z0-9]+)/$ /catalog/product.php?itemNo=$1 [L]
pnoeric
 
Posts: 4
Joined: Sun Apr 16, 2006 5:23 pm

Postby richardk » Wed Nov 08, 2006 5:33 am

I need to add a second variable to be carried through to my script... SO if someone comes to:
http://site.com/catalog/category.php?ca ... newvar=ABC

It should still show it as http://site.com/catalog/category/1014 in the URL bar, but internally, keep 'newvar' and pass it to my php page.

The newvar will be lost when you redirect, it's a whole new request mod_rewrite can't remember anything. You could either redirect to /catalog/category/1014?newvar=ABC or make a new friendly URL like /catalog/category/1014/ABC.
richardk
 
Posts: 8800
Joined: Wed Dec 21, 2005 7:50 am

Postby pnoeric » Sun Nov 12, 2006 8:11 pm

richardk wrote:
I need to add a second variable to be carried through to my script... SO if someone comes to:
http://site.com/catalog/category.php?ca ... newvar=ABC

It should still show it as http://site.com/catalog/category/1014 in the URL bar, but internally, keep 'newvar' and pass it to my php page.

The newvar will be lost when you redirect, it's a whole new request mod_rewrite can't remember anything. You could either redirect to /catalog/category/1014?newvar=ABC or make a new friendly URL like /catalog/category/1014/ABC.


Ah, got it. I'll probably choose the former, then, since that might be easier. Can you show me an example of something like this (rewriting with two variables)? I'll figure it out if someone can give me a shove in the right direction...
pnoeric
 
Posts: 4
Joined: Sun Apr 16, 2006 5:23 pm

Postby richardk » Mon Nov 13, 2006 1:40 pm

Try
Code: Select all
# rewrite old style to new style, make rewrite visible
RewriteCond %{QUERY_STRING} ^(.*&)?itemNo=([A-Za-z0-9]+)(&.*)?$
RewriteRule ^/catalog/product\.php$ /catalog/product/%2/?%1%3 [R=301,L]

# now rewrite new style to old style, make it invisible
RewriteRule ^/catalog/category/([A-Za-z0-9]+)/?$ /catalog/category.php?cat=$1   [QSA,L]
RewriteRule ^/catalog/product/([A-Za-z0-9]+)/?$  /catalog/product.php?itemNo=$1 [QSA,L]
richardk
 
Posts: 8800
Joined: Wed Dec 21, 2005 7:50 am


Return to Idiosyncrasies

Who is online

Users browsing this forum: No registered users and 3 guests

cron