making smart domain general 301 redirects for moved pages using .htaccess - apache

I found my server's c-panel produced code of the following form when I defined redirects using it's web gui. This works fine. However I'm concerned that I could write this much more efficiently, more generally and hopefully with less repetition.
RewriteCond %{HTTP_HOST} ^domain\.com$ [OR]
RewriteCond %{HTTP_HOST} ^www\.domain\.com$
RewriteRule ^some/unwanted/uri$ "http\:\/\/www\.domain\.com\/some/improved/uri" [R=301,L]
RewriteCond %{HTTP_HOST} ^domain\.com$ [OR]
RewriteCond %{HTTP_HOST} ^www\.domain\.com$
RewriteRule ^someother/unwanted/uri$ "http\:\/\/www\.domain\.com\/someother/improved/uri" [R=301,L]
repeat x20
I understand from Jon Lin's answer that repeating the following would usually be necessary.
RewriteCond %{HTTP_HOST} ^domain\.com$ [OR]
RewriteCond %{HTTP_HOST} ^www\.domain\.com$
However hjpotter92 has suggested shortening that to:
RewriteCond %{HTTP_HOST} ^(www\.)?domain\.com$
This is definitely an improvement but how can it be made domain general too. Could that be written as follows to apply to apply more generally to test.domain.com too?
e.g.:
RewriteCond %{HTTP_HOST} ^(*\.)?domain\.com$
I also understand from Jon Lin's answer that I could also replace the following in my code
"http\:\/\/www\.domain\.com\/some/improved/uri" [R=301,L]
with:
"/some/improved/uri" [R=301,L]
Which is gladly both shorter and more general.

Is there any need to keep repeating the following?
Yes, rewrite conditions only apply to the immediately following rule, so any following rules won't have those conditions be applied.
It's not really that inefficient to repeat the conditions, since the conditions aren't even checked unless the rule's pattern matches. That means given this rule:
RewriteCond %{HTTP_HOST} ^peterliddle\.com$ [OR]
RewriteCond %{HTTP_HOST} ^www\.peterliddle\.com$
RewriteRule ^work/cornwall/cornwall_early$ /redirect-target [L,R=301]
If the URL was /blahblah, the rewrite engine first tries to match that URL against the rule's pattern, and since it doesn't match ^work/cornwall/cornwall_early$, the whole rule is skipped, the conditions don't even need to be considered.
If it's a matter of being hard to read, you could have a rule at the very top that bypasses all rules in your htaccess file if those conditions are not met. For example:
RewriteCond %{HTTP_HOST} !^(www\.)?peterliddle\.com$ [NC]
RewriteRule ^ - [L]
RewriteRule ^work/cornwall/cornwall_early$ http://www.peterliddle.com/work/cornwall/cornwall_58-65 [R=301,L]
RewriteRule ^work/cornwall/cornwall$ http://www.peterliddle.com/work/cornwall/cornwall_66-69 [R=301,L]
RewriteRule ^work/whale_moor_a/a_first$ http://www.peterliddle.com/work/whale_moor/lakeland_hills [R=301,L]
So if the conditions for the HTTP HOST fails, the rules are completely bypassed. Problem with doing things this way is you'd have to add any new rules that you don't want the conditions to be applied above these rules.
You can also remove the http://www.peterliddle.com part from your rule's target, since you've already tested for the same domain in the HTTP_HOST.

Following kind advice on stack overflow, testing and some reading I settled on a list of rewrite rules following of the form:
RewriteCond %{HTTP_HOST} (^|\.)domain\.com$
RewriteRule ^some/unwanted/uri$ "/some/improved/uri" [R=301,L]
N.B. The following snippet does not work. It crashes the site and is not recommended.
RewriteCond %{HTTP_HOST} ^(*\.)?domain\.com$
The following seams to work across all subdomains. Therefore I can test in test.domain.com and then use the same .htaccess file in www.domain.com and have greater confidence it will work without having to make further changes to the code.
RewriteCond %{HTTP_HOST} (^|\.)domain\.com$

Related

How to redirect properly

I´m trying to redirect with rewrite rules in Apache .htaccess.
I want to redirect 360invest.pl to 360investment.pl.
But I want one more address on the server which I do NOT want to be redirected. If somebody tries 360invest.pl/bezrzecze it should not redirect to 360investment.pl.
I used this code:
RewriteEngine On
RewriteBase /
RewriteCond %{HTTP_HOST} !^360invest.pl/bezrzecze$ [NC]
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} ^www\. [OR,NC]
RewriteCond %{HTTP_HOST} ^360invest.pl$ [NC]
RewriteRule ^ http://www.360investment.pl%{REQUEST_URI} [L,R=301]
This works only if you try www.360invest.pl/bezrzecze. It doesn't work if you try http://360invest.pl/bezrzecze.
In addition if you click any link on the website (www.360invest.pl/bezrzecze) it will redirect you to www.360investment.pl - it should not.
I'm not sure what I'm missing here. Thank you for your help.
What you are doing is really messy. The first condition does not make sense, because "360invest.pl/bezrzecze" is not a domain name. It is a domain name and a path. The following conditions with [OR] flags make no sense to me.
Think about what you want to accomplish. Start with the general case, then list your exceptions:
If someone goes to 360invest.pl you want to redirect all urls to 360investment.pl
unless someone goes to the path bezrzecze
The first part is the rule. The second part is a condition that has to be met
RewriteCond %{REQUEST_URI} !^/bezrzecze
RewriteRule ^ http://www.360investment.pl%{REQUEST_URI} [L,R]

Issue in execution order with RewriteRules

Due to marketing reasons, I'm using some vanity URL's for friendlier access, and to track some campaigns. Unfortunately, I'm stuck on a managed dedicated server, with cPanel, and these were the steps I took to write my rules:
First, I added xyz.com and efg.com to parked domains in my cPanel
Then I wrote all the RewriteRules that I needed
.htaccess
RewriteCond %{HTTP_HOST} ^xyz\.com$ [OR]
RewriteCond %{HTTP_HOST} ^www\.xyz\.com$
RewriteRule ^signdirections$ "http\:\/\/xyz\.abc\.com\/en?utm_source=signdirections&utm_medium=advert&utm_campaign=xyz" [R=301,L]
RewriteCond %{HTTP_HOST} ^efg\.com$ [OR]
RewriteCond %{HTTP_HOST} ^www\.efg\.com$
RewriteRule ^signdirections$ "http\:\/\/efg\.abc\.com\/en?utm_source=signdirections&utm_medium=advert&utm_campaign=efg" [R=301,L]
Now, the problem is that if I try to access www.efg.com/signdirections, I will get redirected to the www.xyz.com/signredirections version, instead of efg's one.
Any idea, why that is happening? My intuition, is that it detects the same hostname (HTTP_HOST), but I can't understand why.
Most likely it is happening due to your other rules. Better to use THE_REQUEST variable that doesn't change after application of other rules.
You can also combine both your rules into one:
RewriteCond %{HTTP_HOST} ^(?:www\.)?(xyz|efg)\.com$ [NC]
RewriteCond %{THE_REQUEST} /signdirections [NC]
RewriteRule . http://%1.abc.com/en?utm_source=signdirections&utm_medium=advert&utm_campaign=%1 [R=301,L,NE,QSA]
Make sure this is your first rule below RewriteEngine On line.
Make sure to test it in a new browser to avoid old browser cache.
I don't know, if that can be a cache error after a bad test:
How long do browsers cache HTTP 301s?
Just a simplified version:
RewriteCond %{HTTP_HOST} ^(?:www\.)?xyz\.com$
RewriteRule ^signdirections$ http://xyz.abc.com/en?utm_source=signdirections&utm_medium=advert&utm_campaign=xyz [R=302,L]
RewriteCond %{HTTP_HOST} ^(?:www\.)?efg\.com$
RewriteRule ^signdirections$ http://efg.abc.com/en?utm_source=signdirections&utm_medium=advert&utm_campaign=efg [R=302,L]
Try with R=302, and when everything works, change for R=301

How to avoid chain redirections using mod_rewrite?

Basicly i'm working on my site to be SEO-friendly. I wanted to achieve following:
Rewrite urls to pretty ones
Remove multiple slashes (eg. example.com/////something/// to example.com/something/
Redirect www version to a non-www version.
Hide index.php file from all urls
Redirect from old (/?id=something/ to new urls /something/)
I came up with this .htaccess code:
RewriteCond %{THE_REQUEST} //
RewriteRule .* $0 [R=301]
RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
RewriteCond %{QUERY_STRING} ^id=([a-z0-9\/-]+)
RewriteRule ^(.*)$ http://example.com/%1? [R=301]
RewriteRule ^index.php(.*)$ /$1 [R=301]
RewriteRule ^([a-z0-9\/-]+)$ /?id=$1 [L]
...and though it's working it has a side effect: chain redirects, eg. example.com/?id=something////// -> example.com/something////// -> example.com/something/
So is there a way to rewrite or modify this code so it'll be redirecting just once to the preferred version of the url?
Trying to interpret what you want, let's look at the rules in your question:
.1 Can't understand the purpose of this:
RewriteCond %{THE_REQUEST} //
RewriteRule .* $0 [R=301]
.2 This rule-set in your question removes www and converts the query string ?id=val to /val, but only when the incoming URI has www AND there is a query string as both conditions must be met:
RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
RewriteCond %{QUERY_STRING} ^id=([a-z0-9\/-]+)
RewriteRule ^(.*)$ http://example.com/%1? [R=301]
.3 This rule
RewriteRule ^index.php(.*)$ /$1 [R=301]
Hides index.php, but only when it is in the root directory. Example:
http://www.example.com/index.php?id=val
Does not work when it is in a subdirectory. Example:
http://www.example.com/folder/index.php?id=val
.4 Can't understand the purpose of this:
RewriteRule ^([a-z0-9\/-]+)$ /?id=$1 [L]
I suggest this instead:
RewriteEngine On
RewriteBase /
#Redirects all www to non-www
RewriteCond %{HTTP_HOST} www\.example\.com$ [NC]
RewriteRule ^(.*)/?$ http://example.com/$1 [R=301,L]
#Hides "index.php" keeping the query if present
RewriteRule ^(.*)/index\.php$ $1/ [R=301,QSA,L]
#Converts query string `?id=val` to `/val`
RewriteCond %{QUERY_STRING} id=([^/]+)
RewriteRule .* /%1? [R=301,L]
Remember spiders will "adapt" to the correct new structure after a few months, and the problem may ultimately be a whole lot less severe than what it looks like initially. You can leave all the .htaccess code in place, knowing it always be there to correct any "old" references yet will in fact hardly ever actually be used.
I've never found an easy way to avoid multiple round trips back to the client when "fixing up" a URL to be in some sort of canonical form. mod_rewrite seems to be more focussed on the "local" redirect case where the client has no idea that the content it got back came out of a file structure that doesn't perfectly match that implied by the URL.
It is possible to save up all the URL mods locally, then provoke only one round trip to the client that delivers all the URL corrections all at once by setting everything in newly created "environment" variables then at the end asking basically "has anything changed?" However doing so is notably verbose and rather awkward and quite error-prone and has never become a "recommended technique".

Apache Redirect after a RewriteRule

I'm trying to redirect to a "mobile" version of the site, done by the following:
RewriteCond %{HTTP_USER_AGENT} "ipad|iphone|ipod" [NC]
RewriteCond %{HTTP_COOKIE} !^.*mobile.*$ [NC]
RewriteRule ^$ /?m=t [L,R=302,co=mobile:true:.domain.com]
RewriteCond %{HTTP_USER_AGENT} "ipad|iphone|ipod" [NC]
RewriteCond %{HTTP_COOKIE} ^.*mobile=true.*$ [NC]
RewriteCond %{QUERY_STRING} !(^|&)m=t(&|$) [NC]
RewriteRule ^$ /?m=t [L,R=302,co=mobile:true:.domain.com,QSA]
Now this works for the root.
But since the site depends heavily on rewrites, if I modify
RewriteRule ^$ /?m=t [L,R=302,co=mobile:true:.domain.com,QSA]
to
RewriteRule ^(.*)$ /$1?m=t [L,R=302,co=mobile:true:.domain.com,QSA]
It'll give me the correct redirect, but without any previous rewrites.
so if I have say a rewrite previously that was
RewriteRule ^product/shimano$ /index.php?product_cat=shimano [L]
The modified line will give me /index.php?m=t&product_cat=shimano instead of /product/shimano?m=t
What am I missing? I've been trying to figure this out for a while now.
Try redirecting first, then rewriting. That is, put these mobile check rules in front of the rewrites.
That way, if it's mobile, and m=t isn't there, it will do the 302 redirect with m=t added. That will then come through again, skip these rules (since m=t is there), and continue on wiht the normal rewrites.
I'm not entirely sure about the first set of rules above. They might also need that line from the second set, that does the querystring check for m=t, to avoid an infinite loop. Basically, if it already has m=t, then it doesn't do it again.

RewriteCond not working to remove www. from subdomain URL

RewriteEngine On
RewriteCond %{HTTP_HOST} ^www.new.domain.com [NC]
RewriteRule ^(.*)$ http://new.domain.com/$1 [L,R=301]
why when i enter http://www.new.domain.com is not redirected to http://new.domain.com?
The .htaccess file is in the right folder (it has more rewritecond's and they work)
The regexp syntax in your RewriteCond is slightly broken: the correct way to test for strict equality with www.new.domain.com (up to differences in case) is either
RewriteCond %{HTTP_HOST} ^www\.new\.domain\.com$ [NC]
or
RewriteCond %{HTTP_HOST} =www.new.domain.com [NC]
That said, those errors should not stop your rewrite rule from working: you original RewriteCond will match www.new.domain.com just fine, it just matches some other strings too (like wwwXnewYdomainZcomFOOBAR). In fact, I have a very similar set of rules in my own .htaccess file, and they work just fine:
RewriteEngine On
RewriteBase /
RewriteCond %{HTTP_HOST} !^vyznev\.net$
RewriteCond %{HTTP_HOST} ^(www\.)?vyznev\.net$ [NC]
RewriteRule ^(.*) http://vyznev.net/$1 [NS,L,R=permanent]
Most of the differences between your code and mine are purely cosmetic. The only potentially significant issue I can see if that you don't have a RewriteBase directive; you should definitely add one, if only to avoid potential problems later. Still, as far as I can tell, not having one shouldn't stop you from getting at least some redirect, even if it might not be to the URL you expect.