RewriteRule to handle one domain two folders to two domains no folder - regex

I am attempting to create rewrite rules to handle some specific website redirections:
I would like domain1.ca/folder1/xyz to go to domain2.ca/xyz and domain1.ca/folder2/xyz to go to domain3.ca/xyz
Right now my attempts are as following:
RewriteCond %{HTTP_HOST} ^domain1.ca$ [OR]
RewriteCond %{HTTP_HOST} ^www.domain1.ca$
RewriteRule ^(\/folder1\/)(.*)$ "https://domain2.ca/$1" [R=301,L]
RewriteCond %{HTTP_HOST} ^domain1.ca$ [OR]
RewriteCond %{HTTP_HOST} ^www.domain1.ca$
RewriteRule ^(\/folder2\/)(.*)$ "https://domain3.ca/$1" [R=301,L]
Any help would be greatly appreciated :) Thx.

A couple of problems with your existsing rules:
In .htaccess the URL-path matched by the RewriteRule pattern does not start with a slash. So, the URL-path starts folder1/xyz, not /folder1/xyz.
You are unnecessarily capturing "folder1" in the first parenthesised subpattern and using this in the substitution string (ie. $1). You should be using $2, or don't capture the first path segment.
The directives could also be tidied up a bit (eg. no need to backslash-escape slashes in the regex and the conditions can be combined).
Try the following instead:
RewriteCond %{HTTP_HOST} ^(www\.)?domain1\.ca [NC]
RewriteRule ^folder1/(.*) https://domain2.ca/$1 [R=301,L]
RewriteCond %{HTTP_HOST} ^(www\.)?domain1\.ca [NC]
RewriteRule ^folder2/(.*) https://domain3.ca/$1 [R=301,L]
Additional notes:
The end-of-string anchor ($) following (.*)$ in the RewriteRule pattern is not required since regex is greedy by default.
You only need to surround the argument in double quotes if it contains spaces.
I removed the end-of-string anchor ($) from the end of the CondPattern to also match fully qualified domain names that end in a dot.
I added the NC flag to the condition. It's technically possible that some bots can send a mixed/uppercase Host header.
Test first with 302 (temporary) redirects to avoid potential caching issues.

Related

Query string not redirected on deeper level directories

I want to redirect each link with a query string to a specific address by appending the string. I have the following in my WordPress .htaccess:
RewriteEngine On
RewriteCond %{QUERY_STRING} catid=([0-9]+) [NC]
RewriteRule (.*) catid%1? [R=301,L]
When a user hits example.com/?catid=10, they are successfully redirected to example.com/catid10, which is what I want.
However, when they go a directory deeper (example.com/category/?catid=10), they are not redirected to example.com/category/catid10.
I have been reading manuals but can't find the answer.
Edit
If this is helpful, this is what WordPress has defined in my htaccess:
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
Providing RewriteBase / is already defined (which it normally is with WordPress and must be if your existing redirect is working) then you can do it like the following with a single rule:
RewriteCond %{QUERY_STRING} (?:^|&)catid=(\d+) [NC]
RewriteRule (.*?)/?$ $1/catid%1 [QSD,R=301,L]
The capturing group (.*?) is non-greedy so does not consume the optional trailing slash that follows.
Requests for the document root do not require RewriteBase, but all "deeper" URLs do, since the resulting substitution string will otherwise be a relative URL.
The non-capturing (?:^|&) prefix on the CondPattern ensures that it only matches the URL parameter name catid and not foocatid etc.
You just need to add the captured group from (.*) to your redirect target using $1. I'd break it up into a few rules so that you don't get duplicated slashes. Rather then ending your rewrite targets with a question mark, I would add the QSD (query string discard) flag to the rule. I would also add starts with (^) and ends with ($) to rewrite rule so you always match the whole thing. I also like to start my rules with an optional slash (/?) so that the rules can be used in both .htaccess and apache conf.
RewriteEngine On
# Home URL with catid query string
RewriteCond %{QUERY_STRING} catid=([0-9]+) [NC]
RewriteRule ^/?$ /catid%1 [R=301,L,QSD]
# Deep URL ending in slash with a catid query string
RewriteCond %{QUERY_STRING} catid=([0-9]+) [NC]
RewriteRule ^/?(.*)/$ /$1/catid%1 [R=301,L,QSD]
# Deep URL not ending in slash with a cadid query string
RewriteCond %{QUERY_STRING} catid=([0-9]+) [NC]
RewriteRule ^/?(.*)$ /$1/catid%1 [R=301,L,QSD]

htaccess RewriteRule regex

I have hundreds of these old links I need to redirect.
Here is one example:
/index.php?option=com_content&view=article&id=433:seventh-character-code-categories-and-icd-10-cm&Itemid=101&showall=1
to
/seventh-character-code-categories-and-icd-10-cm
Essentially I need to remove the /index.php?option=com_content&view=article&id=433: part.
I tried this but I am getting confused with the [0-9] and : parts, so the following does not work:
RewriteRule ^/index.php?option=com_content&view=article&id=[0-9]:(.*)$ /$1 [L,R=301]
Say you want to capture from after : to right before & in the query string you mentioned, then try this expression:
^[^\:]*\:([^\&]*)\&.*$
As #starkeen mentioned in comments, you got to check against the query string. This can be done using RewriteCond %{QUERY_STRING}
So if index.php is in the root folder:
RewriteEngine On
RewriteCond %{REQUEST_URI} ^\/index\.php$
RewriteCond %{QUERY_STRING} ^[^\:]*\:([^\&]*)\&.*$
RewriteRule ^(.*)$ http://example.com/%1 [R=301,L]
Here's another example. This one is for a sub folder:
RewriteEngine On
RewriteCond %{REQUEST_URI} ^\/pages\/index\.php$
RewriteCond %{QUERY_STRING} ^[^\:]*\:([^\&]*)\&.*$
RewriteRule ^(.*)$ /pages/%1? [R=301,L]
Also, notice the ? at the end of the url /pages/%1?, this prevents from re-attaching the query string.
Another thing, captured groups will be set to variables %{number} since set in the RewriteCond.
BTW, depending on your server's configuration, you may need to add the NE flag, like [NE,L,R=301] Plus test whether it is necessary to double escape the literal characters.
what is about direct approach. Skip all till semicolon, mach string till & and replace all with first much
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{QUERY_STRING} [^:]+:([\w-]+[^&]).*
RewriteRule .*$ \/%1? [R=301,L]
</IfModule>

How this regex matches in htaccess

I am trying to add trailing slash to a url. I have my own logic to do so but found another one on stackoverflow (here). Now the regex in this line
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]
apparently matches the RewriteRule for the url http://www.example.com/wp-admin.
What I first did was:
RewriteCond %{REQUEST_URI} /wp-admin$ [NC]
RewriteCond %{REQUEST_URI} !/+$
RewriteRule ^ %{ENV:proto}://%{HTTP_HOST}%{REQUEST_URI}/ [R=301,L]
So my question is how /wp-admin$ is similar to ^([_0-9a-zA-Z-]+/)?wp-admin$?
This is because leading slash is matched when you use RewriteCond %{REQUEST_URI} but current directory (relative to DocumentRoot) is stripped when you use a pattern in RewriteRule in .htaccess file, which is a per directory directive.
Hence a leading slash is not matched in RewriteRule but is matched in RewriteCond.

.htaccess: append subdomain as query to existing queries

I'm trying to append the subdomain as a query to eventual existing queries using htaccess.
http://test.domain.com should be http://test.domain.com?x=test
http://test.domain.com?id=1 should be http://test.domain.com?id=1&x=test
This is what I have done, but it doesn't work and I can figure out why:
RewriteCond %{HTTP_HOST} ^([a-z0-9_-]+)\.domain\.com$ [NC]
// exclude www.domain.com
RewriteCond %1 !^(www)$ [NC]
RewriteRule ^[^\?]*(?:\?(.*))?$ index.php?$1&x=%1 [L]
My understanding was
[^\?]* all characters except ?, match 0 or more times
(?: start of a non capturing group
\? match ? literally
(.*) all characters after ? as a group
)? end of the non capturing group, match 0 or 1 times
But it does not work. Where is my mistake?
UPDATE 1:
I could make it work by using the following rule
RewriteRule (.*) index.php?$1&x=%1 [QSA,L]
http://test1.domain.com?y=test1 brings me [x=>test1,y=>test2]
but
http://test1.domain.com?y=test1&x=test3 brings me [x=>test3,y=>test2]
So it overrides my x value. Is there a way to block that?
UPDATE 2
This is the code I'm using now:
RewriteEngine On
RewriteCond %{HTTP_HOST} ^(?!www)([\w-]+)\. [NC]
RewriteCond %1::%{QUERY_STRING} !^(.+?)::x=\1(?:&|$) [NC]
RewriteRule ^ index.php?%{QUERY_STRING}&x=%1 [L]
Try this rule:
RewriteEngine On
RewriteCond %{HTTP_HOST} ^(?!www)([\w-]+)\. [NC]
RewriteCond %1::%{QUERY_STRING} !^(.+?)::x=\1(?:&|$) [NC]
RewriteRule ^ index.php?%{QUERY_STRING}&x=%1 [L]
Make sure this is the only rule you have in .htaccess while testing.
Explanation of:
RewriteCond %{HTTP_HOST} ^(?!www)([\w-]+)\. [NC]
RewriteCond %1::%{QUERY_STRING} !^(.+?)::x=\1(?:&|$) [NC]
We are capturing starting part of hostname from this group: ([\w-]+) which is denoted by %1. Note that we cannot use %1 in RHS of a condition.
We are then appending %1 and %{QUERY_STRING} together in %1::%{QUERY_STRING}. Here we could use any other arbitrary delimiter like ## as well.
In RHS we have ^(.+?)::x=\1(?:&|$) which means %1 followed by delimiter :: followd by literal x= and then \1 which is back-reference for %1 (goup before ::). ! before ^ is there to negate the condition. In simple words this condition means execute this rule only if we already don't have x=subdomain in QUERY_STRING.
Looks like you are trying to match the query string content with your RewriteRule’s pattern – that is not possible, it searches only the path component of the requested URL.
But, no worries – there’s an easy solution that helps combine the original query string, and what the pattern matched: The QSA flag.
So this should do the trick (combined with your existing RewriteConds):
RewriteRule (.*) index.php?$1 [QSA,L]

How to Redirect Subdomains to Other Domain

What I'm trying to accomplish with htaccess mod-rewrite:
Redirect all sub-domains to new domain name w rewrite rule.
e.g.
test1.olddomain.com ===> test1.newdomain.com
test2.olddomain.com ===> test2.newdomain.com
test3.olddomain.com ===> test3.newdomain.com
This is what I have so far which of course is wrong:
Options +FollowSymLinks
RewriteEngine on
RewriteCond %{HTTP_HOST} ^olddomain\.com$ [NC]
RewriteRule ^(.*)$ http://www.newdomain.com/$1 [R=301,L]
RewriteCond %{HTTP_HOST} ^www\.olddomain\.com$ [NC]
RewriteRule ^(.*) http://www.newdomain.com/$1 [R=301,L]
RewriteRule [a-zA-Z]+\.olddomain.com$ http://$1.newdomain.com/ [R=301,L]
Since I'm not a Regular Expression junkie just yet, I need your help... Thanks for any help you can give here. I know also we can compile these first two conditions into one.
Note: The reason I don't redirect all domain using DNS is that a lot of directories need special rewrite rules in order to maintain positions on SEO.
In .htaccess files, the "URL" that RewriteRules match has been stripped of the domain name and any directories that led to the current directory. (Using mod_rewrite in .htaccess files is a huge pain; if you have access to the server conf do it there instead!!)
So, assuming that your .htaccess is in your DocumentRoot, try something like this:
RewriteEngine on
RewriteCond %{HTTP_HOST} ^(.*)olddomain\.com$ [NC]
RewriteRule ^(.*)$ http://%1newdomain.com/$1 [R=301,L]
The %1 is supposed to match the first group in the RewriteCond and the $1 is supposed to match the URL part.
RewriteRule ^(.+)\.olddomain\.com$ http://$1.newdomain.com/ [R=301,L]
You need to specify the ^ at the beginning to ask the regex engine to match a line beginning there. Next, you match anything before ".olddomain.com" and assign that to the first matched pattern (which will later be accessible in $1). You need to surround with parentheses (.+) in order for the match to be assigned to $1.