mod_rewrite - how to redirect a mistyped 'pret

2019-07-21 15:38发布

I have a site written in php which creates 'pretty' urls for each item (on category and search pages) like this,

mysite.com/category/slug-for-item-one/1
mysite.com/category/slug-for-item-two/2

The /category/ and /slug/ is dependent upon the numeric id of the item

I have mod_rewrite serve the actual content from urls like this:

mysite.com/view-item.php?id=1
mysite.com/view-item.php?id=2

The content for each item is retrieved using just the items id.

Here's my htaccess:

RewriteEngine on
RewriteRule ^([^/\.]+)/?$ view-item.php?id=$1 [L]
RewriteRule ^([^/\.]+)/([^/\.]+)/?$ view-item.php?pid=$2 [L]
RewriteRule ^([^/\.]+)/([^/\.]+)/([^/\.]+)/?$ view-item.php?id=$3 [L]

Everythings ok so far but, if someone lands on on a url like,

mysite.com/1
mysite.com/catey/slug-for-item-one/1 

or

mysite.com/category/slug-item-one/1

the content is still served, but how can I automatically reset or redirect to the canonical version of the url, to:

mysite.com/category/slug-for-item-one/1

I've searched SO and google extensively for an answer, but no luck. I've only used mod_rewrite for simple redirects such as from without www. to with www. and my understanding is tentative thus I'm struggling to understand how to proceed at the moment.

Can anyone point me in the right direction?

Thank you everyone for your help. Much appreciated. I'm working on an implementation of Jon Lin's answer as I'm more familiar with using php/mysql databases and understand how and why it should work. I aim to be done by Friday and will update this page when finished. Many thanks, Karl.

* UPDATE * I have implemented Jon Lin's answer and now my 'pretty' urls, when mistyped are now redirected to the correct or 'canonical' url just as on SO. Thank you Jon and everyone who contributed!

3条回答
Explosion°爆炸
2楼-- · 2019-07-21 16:18

I think you are going to need two sets of rewrite rules to accomplish this.

The first set of rules would be used to send 301 redirects to the client to ensure they are referencing the canonical URLs:

RewriteRule ^/1    /category/slug-for-item-one/1 [R=301,L]

Then a second set of rules that use passthroughs [PT] to serve up the content:

RewriteRule ^([^/\.]+)/([^/\.]+)/([^/\.]+)/?$ view-item.php?id=$3 [PT,L]

Or something along those lines...

查看更多
我欲成王,谁敢阻挡
3楼-- · 2019-07-21 16:20

This is probably something you want to implement in your view-item.php instead of trying to use mod_rewrite. When you are generating the links internally within your content, you can use the ID's to lookup categories and slugs. This would be how you normally go about generating one of the pretty SEO friendly links.

You first need pass the category and slug into the request. Something along the lines of:

RewriteEngine on
RewriteRule ^([^/\.]+)/?$ view-item.php?id=$1 [L]
RewriteRule ^([^/\.]+)/([^/\.]+)/?$ view-item.php?pid=$2&cat=$1 [L]
RewriteRule ^([^/\.]+)/([^/\.]+)/([^/\.]+)/?$ view-item.php?id=$3&cat=$1&slug=$2 [L]

At the top of your view-item.php, simply check if the cat and slug parameters exist, compare them to the actual category and slug when you do the lookup for the id. If one of them doesn't match (or is missing, if you want), then redirect the browser to the correct link with the correct category and slug using the header() function:

header('HTTP/1.1 301 Moved Permanently');
header("Location: " . $correct_url);
exit();

After they get redirected, the process repeats itself, but this time, view-item.php sees the correct category and slug so the page gets served like normal.

查看更多
欢心
4楼-- · 2019-07-21 16:33

Easy as pie:

RewriteCond %{REQUEST_URI} ^([^/\.]+)/([^/\.]+)/([^/\.]+)/$
RewriteCond %1 !category
RewriteRule ^([^/\.]+)/([^/\.]+)/([^/\.]+)/$ $1/category/$3/

This means: if the request does look like category/slug-for-item-two/2 and the first match is not the word category (whatever it is) then force the redirect to category/slug-for-item-two/2

Please tell me if it works


Update (after your comment):

Here's what should work:

Create 2 map files (see mod_rewrite.html#rewritemap to learn how to do).

Create a mapfile where you put all the categories you need:

RewriteMap mapcategories \
  dbm:/web/htdocs/yoursite/rewriterules/categories.map

In the mapfile create simple entries like:

shirts 1
hats 2
condoms 3
vegetables 4
mother-in-laws 5
...

Now do another file with the opposite:

RewriteMap mapcategoriesreverse \
  dbm:/web/htdocs/yoursite/rewriterules/categoriesreverse.map

In the mapfile create simple entries like:

1 shirts
2 hats
3 condoms
4 vegetables
5 mother-in-laws
...

Then here you go for the hard part:

RewriteCond %{REQUEST_URI} ^([^/\.]+)/([^/\.]+)/([^/\.]+)/$
# The following rule will try to search into the categories map file
# and if not found, assign CATEGORY to "notfound"
RewriteRule ^([^/\.]+)/([^/\.]+)/([^/\.]+)/$ \
    - [QSA,E=CATEGORY:${mapcategories:%1|notfound}]

# if the CATEGORY is not empty and is not found:
RewriteCond %{ENV:CATEGORY} notfound
# do a reverse map to get the *real* category:
RewriteRule ^([^/\.]+)/([^/\.]+)/([^/\.]+)/$ \
    - [QSA,E=CATEGORYREVERSE:${mapcategoriesreverse:%1|notfound}]

# if the CATEGORYREVERSE is not empty and is not found:
RewriteCond %{ENV:CATEGORYREVERSE} notfound
# this should never happen => 404:
RewriteRule . - [R=404,L]

# If reach here = if the CATEGORYREVERSE is not empty
# this means it has properly been found:
RewriteCond %{ENV:CATEGORYREVERSE} !^$
# Inject the right category:
RewriteRule ^([^/\.]+)/([^/\.]+)/([^/\.]+)/$  \
    %{ENV:CATEGORYREVERSE}/$2/$3/ [QSA]

This way everything is dynamic but it's (much) longer and (a little bit) more complex.

Olivier

查看更多
登录 后发表回答