Apache Redirect after a RewriteRule - apache

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.

Related

How do I Redirect and Show Content with same urls?

I have a maybe simple Problem. I have serveral URLs that needed to be redirected in this way:
if the URL "/abc/" is called, it should show the content located under "xyz.html"
i can do that with
RewriteRule abc$ xyz.html
but "xyz.html" should be 301 redirecting to "/abc" if it is called.
This is my simple problem i am searching for an solution since hours.
it would be easy if its like "test.html" and /test/. i can do it like
RewriteRule ^(.+)\.html$ /$1 [L,R=301]
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule !.*\.html$ %{REQUEST_URI}.html [END]
But in my case i have a bunch of URLs with no pattern.
All i tried results to server misconfiguration.
Can you help me with that one sample?
thanks, kanuddel
Could you please try following.
RewriteEngine ON
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{REQUEST_URI} ^/abc/? [NC]
RewriteRule ^(.*) /xyz.html [L]
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{REQUEST_URI} ^/xyz\.html/? [NC]
RewriteRule ^(.*) /abc/? [R=301,L]
Detailed Explanation:
Why there is redirection loop with normal rule writing: Question from OP is interesting and contradictory(kind of), why because lets say we write 1st rule to redirect(in backend) from abc url to xyz.html it will work fine. But then when we write our 2nd rule which is to rewrite url from xyz.html to abc/ which is exactly opposite of first rule. Hence it becomes a loop, ASAP 1st rules gets served it reaches out to 2nd rule and it serves it back to 1st rule thus a REDIRECTION INFINITE LOOP(till 50 cycles or so).
How to prevent redirection loop?
First since both rules are exactly opposite of each other so in a normal rule writing it will become a loop, now how could we prevent it to become a loop? Answer is using: RewriteCond(explained in next step)
I have used an ENVIRONMENT VARIABLE named ENV:REDIRECT_STATUS which will have any redirection status in its value. 2 things we need to understand about this variable.
1- Without any request redirection its default value is NULL.
2- Whenever we do a successful redirection its value becomes 200(NON ZERO), which we can make use of in our conditions part.
Detailed explanation of htaccess Rules:
Now coming to the condition explanation part: In RewriteRuleRewriteRule ^(.*) /xyz.html [L] I have NOT done any rewriting of url on browser why because of the THUMB RULE that we always want to show USER FRIENDLY URLs to users, so environment variable ENV:REDIRECT_STATUS will always be ZERO here.
Coming to 2nd RewriteRule now RewriteRule ^(.*) /abc/? [R=301,L] where user already using a user NON-friendly URL so first thing is I need to rewrite URL in browser to user friendly url hence R=301(redirection with permanent flag is used here). Once Redirection happens through this condition, 1st condition will start failing now why because that checks if REDIRECT_STATUS variable is NULL which is NOT after serving redirection from 2nd condition. Hence this is how it prevents loop by this small trick :)
Thanks for the great Explanation!
I tried it with a second URL, where "/xxx/" should show "zzz.html" But this gave me an Misonfiguration. I tried to shorten it like this:
RewriteEngine ON
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{REQUEST_URI} ^/abc/? [NC]
RewriteRule ^(.*) /xyz.html [L]
RewriteCond %{REQUEST_URI} ^/xxx/? [NC]
RewriteRule ^(.*) /zzz.html [L]
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{REQUEST_URI} ^/xyz\.html/? [NC]
RewriteRule ^(.*) /abc/? [R=301,L]
RewriteCond %{REQUEST_URI} ^/zzz\.html/? [NC]
RewriteRule ^(.*) /xxx/? [R=301,L]

Remove part of the query string with mod_rewrite

I am not very good with .htaccess at all, so I want to achieve something very simple, but I can't. What I want to do is to redirect certain files to test.php, and if test is ok, PHP redirects back to original page. It works fine, I add the "test=ok" part to the original URL, that way I don't get a redirect loop. However, I want to remove the test=ok query part from the original URL on redirection. How can I achieve that???
TL/DR
I have several URLs I want rewritten through mod_rewrite.
examples:
http://example.com/?time=1&test=ok
http://example.com/?test=ok
How can I remove the &test=ok and the ?test=ok parts using .htaccess?
Right now I have:
RewriteCond %{QUERY_STRING} ^test=ok$ [NC]
RewriteRule (.*) /$1? [L]
RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{REQUEST_URI} (/[^.]*|\.(php|html?|js))$ [NC]
RewriteCond %{QUERY_STRING} !test=ok [NC]
RewriteRule .* test.php [L]
But that doesn't remove the test=ok part... :(

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

Apache mod_rewrite accept language subdomain redirection

i have a multilingual site with 3 languages and i'm using the following rules to redirect requests to the right version of the website based in browser accept language.
#swedish
RewriteCond %{HTTP:Accept-Language} ^sv.*$ [NC]
RewriteCond %{REQUEST_URI} ^/$ [NC]
RewriteCond %{QUERY_STRING} !(^q\=) [NC]
RewriteRule ^(.*)$ /sv [L,R=301]
#norwegian bokmal
RewriteCond %{HTTP:Accept-Language} ^nb.*$ [NC]
RewriteCond %{REQUEST_URI} ^/$ [NC]
RewriteCond %{QUERY_STRING} !(^q\=) [NC]
RewriteRule ^(.*)$ /nb [L,R=301]
#norwegian
RewriteCond %{HTTP:Accept-Language} ^no.*$ [NC]
RewriteCond %{REQUEST_URI} ^/$ [NC]
RewriteCond %{QUERY_STRING} !(^q\=) [NC]
RewriteRule ^(.*)$ /nb [L,R=301]
#all others go to english
RewriteCond %{REQUEST_URI} ^/$ [NC]
RewriteCond %{QUERY_STRING} !(^q\=) [NC]
RewriteRule ^(.*)$ /en [L,R=301]
I need to upgrade this rules to do the same redirections keeping subdomains too
Example for a norwegian request:
www.domain.com/subdomain -> www.domain.com/nb/subdomain
How can i achieve this?
I have thank #faa for helping me out with the current rules.
UPDATE
Question Description
Regarding the pages vs subdomains issue in your comments, in this specific case is irrelevant. The fact is the incoming URL in your question is: www.domain.com/subdomain and, except for the name, /subdomain is clearly a page.
A real subdomain is indeed a domain that is part of another domain and the URL format that holds it, is something like subdomain.domain.com, so there was no confusion on my part.
What it is not clear enough, is how the redirection will be handled. According to your question, but replacing "subdomain" with the whole path, here are some examples:
www.domain.com/ should go to www.domain.com/LangCode/, (previous working redirection)
www.domain.com/page should go to www.domain.com/LangCode/page
www.domain.com/page1/page2/page3/etc/ should go to www.domain.com/LangCode/page1/page2/page3/etc. This possibility is not in the question, but eventually could be neded.
In those cases, the pages in the incoming and redirected URLs should have the same name, but, although in the incoming URL do not have to exist, in the redirected URL the pages MUST exist and so a loading default script (index.php or index.html, for example) to handle the request.
Which means, there has to be a script in each page subject to redirection. I would say at least 2 for each language.
As far a I understand, that's what the question and complementary comments indicate, but it seems it is not a practical approach.
Suggested Solution
A better approach could be a single script at the root folder that handles all requests. This is an idea that can be better described with examples:
www.domain.com/ always showing in the browser's address bar but going internally to
www.domain.com/lang_handler.php?lang=sv or
www.domain.com/page1/ always showing in the browser's address bar but going internally to www.domain.com/lang_handler.php?lang=sv&target_page1=page1
This can be achieved in .htaccess with mod_rewrite directives. Here is an example:
RewriteEngine On
RewriteBase /
# Set managed languages here, except default (en)
RewriteCond %{HTTP:Accept-Language} ^(sv|ne|no).*$ [NC]
# Replace the names of the script and the parameters in the next 2 lines
RewriteCond %{REQUEST_URI} !lang_handler\.php [NC]
RewriteRule ^([^/]+)?/?$ lang_handler.php?lang=%1&target_page1=$1 [L,QSA]
# If no match, set English
# Replace the names of the script and the parameters in the next 2 lines
RewriteCond %{REQUEST_URI} !lang_handler\.php [NC]
RewriteRule ^([^/]+)?/?$ lang_handler.php?lang=en&target_page1=$1 [L,QSA]
The above rule set maps silently
http://www.domain.com/ or http://www.domain.com/page1
To
http://www.domain.com/lang_handler.php?lang=LangCode&target_page1=page1
Where LangCode is sv ne no or en by default.
This example only works for 1 page or no page. Any number of pages can be handled though, but the rules have to be modified accordingly. More parameters and regex groups have to be added to the RewriteRules.
$1 should do the trick.
RewriteCond %{HTTP:Accept-Language} ^no.*$ [NC]
RewriteCond %{REQUEST_URI} ^/$ [NC]
RewriteCond %{QUERY_STRING} !(^q\=) [NC]
RewriteRule ^(.*)$ /nb/$1 [L,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".