0

Website has changed its url names due to SEO reasons, e.g. it was:

/category/filter1/f00/filter2/123/filter3/100-500/filter4/36.html

now:

/category/color/red/size/big/price/100-500/style/classic.html

I know the old and new names, they're fixed. Please help me to build a rewrite rule which will result in 301 redirect from old urls to new. I did research and I see that I cannot make it using RewriteMap for example, so I ended up making something like RewriteRule (.*)filter1(.*) $1color$2 [L] etc. Not only I don't like the way it looks, but also it doesn't give me a 301 redirect.

UPDATE: Note that at the moment I have several rules, one per filter name/value, e.g.:

RewriteEngine on

# make sure it's a catalog URL, not anything else
RewriteCond %{REQUEST_URI} !^/(category1|category2|category3|category4)
RewriteRule .* - [L]

# rewrite filter names
RewriteRule (.*)filter1(.*) $1color$2 [L]
RewriteRule (.*)filter2(.*) $1price$2 [L]
...etc...

It works as expected - changing all the names in URL, but setting R flag causes the stop on first rule and redirect to URL like:

/var/www/vhosts/site/htdocs/category/color/red/filter2/123/ etc...

I separated rules because any of filters may or may not exist in the URL. I will greatly appreciate the better solution.

Martin
  • 337
  • 2
  • 17

2 Answers2

2

Here is my own answer: it is possible to do with environment variables. We need to replace old filter names and values with new ones, and then make only one 301 redirect to new URL. Here what I've done using mod_rewrite and environment variables:

RewriteEngine on

RewriteRule /filter1/               - [E=filters:/color/]
RewriteRule /f00[.\/]               - [E=filters:%{ENV:filters}red]
RewriteRule /0f0[.\/]               - [E=filters:%{ENV:filters}green]
RewriteRule /00f[.\/]               - [E=filters:%{ENV:filters}blue]

RewriteRule /filter2/               - [E=filters:%{ENV:filters}/size/]
RewriteRule /123[.\/]               - [E=filters:%{ENV:filters}big]
RewriteRule /32[.\/]                - [E=filters:%{ENV:filters}small]

RewriteRule /filter3/([^/^\.]+)     - [E=filters:/price/$1]

RewriteRule /filter4/               - [E=filters:%{ENV:filters}/style/]
RewriteRule /36[.\/]                - [E=filters:%{ENV:filters}classic]
RewriteRule /37[.\/]                - [E=filters:%{ENV:filters}urban]

RewriteCond %{REQUEST_URI} ^/(category1|category2|category3|category4)/
RewriteCond %{ENV:filters} !^$
RewriteRule ^([^/]+)/ /$1%{ENV:filters}.html [L,R=301]

Basically, I've reformatted whole the URL in environment variable filters then checked if it's a category and not some else part of the website, and finally made redirect to this category+filters variable, appended .html at the end.

Martin
  • 337
  • 2
  • 17
0

Even though the new URL looks prettier to a human, I'm not sure if there's a need to change the existing URL for SEO reasons.


To get a redirect instead of a rewrite, you must use the R|redirect flag. So your rule would look like

RewriteRule (.*)filter1(.*) $1color$2 [R,L]

But if you have multiple redirects, this might impact your SEO results negatively, see Chained 301 redirects should be avoided for SEO , but Google will follow 2 or 3 stacked redirects

  • Remember that ideally you shouldn’t have any stacked redirects or even a single redirect if you can help it, but if required Google will follow chained redirects
  • But every additional redirect will make it more likely that Google won’t follow the redirects and pass PageRank
  • For Google keep it to two and at a maximum three redirects if you have to
  • Bing may not support chained redirects at all

This means try to replace multiple filters at once

RewriteRule ^(.*)/filter1/(.*)/filter2/(.*)$ $1/color/$2/size/$3 [R,L]

and so on.


When the filters may come in an arbitrary order, you may use several rules and do a redirect at the end

RewriteRule ^(.*)filter1(.*)$ $1color$2 [L]
RewriteRule ^(.*)filter2(.*)$ $1price$2 [L]
RewriteRule ^(.*)filter3(.*)$ $1size$2 [L]

RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule ^ %{REQUEST_URI} [R,L]

RewriteCond with REDIRECT_STATUS is there to prevent an endless loop.


When it works as it should, you may replace R with R=301. Never test with R=301.

A final note, be very careful with these experiments. I managed to kill my machine twice (it became unresponsive and I had to switch off) during tests.

Community
  • 1
  • 1
Olaf Dietsche
  • 72,253
  • 8
  • 102
  • 198
  • Thank you for the detailed answer. As to "SEO reasons" - some of the URLs were in my native language, and it's proved by experiments that it's better for SEO to translate it to english letters (transliteration). As to the chains - why should it result in a redirect chain? There was an old URL and a new URL, and I set a 301 redirect between them. The new page should not contain old URLs so there's no need for Google to do any other redirect. Correct me please. As to `R` flag - I've been trying to set it, but I have several rules (see post updates) and it stops after the first rule. – Martin Jun 20 '16 at 09:54
  • When you have separate rules for each filter, e.g. filter1 R -> filter2 R -> filter3 R -> filter4 R, you have 4 redirects. This would slow down page loading (bad for user experience), because you have 4 round trips, and also a redirect chain, which *might* affect your page rank. Disclaimer, I'm not an SEO expert. – Olaf Dietsche Jun 20 '16 at 11:46
  • No doubt, I don't need more than one redirect. Can this be done just by one? Or is it impossible with mod_rewrite? – Martin Jun 20 '16 at 13:01
  • Thanks again, your solution seems better, I'll test it next days, the only question I have is about halting of your machine - could you please give more details about what did you do to make machine halt? It's important to understand and not to make these things. – Martin Jun 24 '16 at 12:08
  • Since my first approaches didn't work, I tried the [N|next](https://httpd.apache.org/docs/current/rewrite/flags.html#flag_n) flag and didn't restrict the number of loops, e.g. `[N=10]`. The default is 32000 however, and I guess Apache took all my memory. – Olaf Dietsche Jun 24 '16 at 13:35
  • I tested your last solution, it looks more elegant than mine, and it solves one of problems that I have in my real task (a "trailing dash", because I have multiple values of filters separated by a dash, which I have to translate also), but it makes several internal recursions (caused by [L] flag), which means that you have to increase the LimitInternalRecursion in Apache's httpd.conf (default value is 10) if you have more than 10 rewrite rules, and I received a comment from our devops that it's undesirable as it can increase TTFB. – Martin Jun 30 '16 at 16:41
  • But your solution works so far, doesn't it? You could remove the trailing dash from your env variable in a final rule. – Olaf Dietsche Jun 30 '16 at 18:46
  • Thanks again for a fresh idea with trailing dash! – Martin Jun 30 '16 at 22:26