I'm trying to rewrite some urls but my newbness is getting in the way.
URLs input into the address bar
example.com/prefix-suffix.html
example.com/prefix-suffix.html?v=huzzah
Desired output (would like them displayed like)
example.com/prefix/suffix/
example.com/prefix/suffix/huzzah
I've looked at a few other examples like this one, but I just don't understand what they are doing exactly or how rather, which makes it challenging to modify.
If you have the time to explain what each line is doing I would greatly appreciate it.
Thanks.
You can use this code in your DOCUMENT_ROOT/.htaccess file:
RewriteEngine On
RewriteBase /
RewriteCond %{THE_REQUEST} \s/+([^-]+)-([^.]+)\.html\s [NC]
RewriteRule ^ /%1/%2/ [R=302,L,NE]
RewriteCond %{THE_REQUEST} \s/+([^-]+)-([^.]+)\.html\?v=([^\s&]+) [NC]
RewriteRule ^ /%1/%2/%3? [R=302,L,NE]
RewriteCond %{REQUEST_FILENAME} -d [OR]
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^ - [L]
RewriteRule ^([^/]+)/([^/]+)/?$ /$1-$2.html [L]
RewriteRule ^([^/]+)/([^/]+)/([^/]+)/?$ /$1-$2.html?v=$3 [L,QSA]
this is a good reference. to summarize the relevant parts:
in the SO question you are referring to, the OP wanted index.php?page=mobile to become index/page/mobile
and the accepted answer to that question was:
RewriteEngine on
RewriteBase /cashearn/
RewriteCond %{THE_REQUEST} /index\.php\?page=([^\s&]+) [NC]
RewriteRule ^ index/page/%1? [R=302,L]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^index/page/([^/]+)/?$ index.php?page=$1 [L,QSA,NC]
in order to understand how that is working, let's take a close look at this section:
RewriteCond %{THE_REQUEST} /index\.php\?page=([^\s&]+) [NC]
RewriteRule ^ index/page/%1? [R=302,L]
RewriteCond is a condition that must be matched in order for the subsequent RewriteRule to be used.
the pattern-matching is done using regex. the basics are easy to master so i recommend that you look it up. in any case, anything of the form %N (e.g. %1) is a backreference to the "grouped" part of the pattern of the last RewriteCond in the current set of conditions. A "grouped" part is denoted by parentheses. In other words, the %1 in the RewriteRule refers to the ([^\s&]+) in the RewriteCond.
looking at the regex:
square brackets in regex denote a character class so [^\s&] is thus a character class.
the caret ^ when it is inside a character class denotes negation. \s is an escape code for the whitespace character. so, all in all, [^\s&] means "any character except whitespace and &". the character class is appended with + which means "one or more". so, the regex pattern will match one of characters that are included in the character class. for a url, this essentially means any combination of letters, digits and %.
the other characters in the RewriteRule and RewriteCond, other than the "server-variable" %{THE_REQUEST}, are either regex special characters or literals. (by literals i mean that ab in a regex will match the string cab.)
^, the caret, when it isn't inside a character class, is a special character that denotes the beginning of a line. note that ? and . are also literals here, despite the fact that they are included in the list of regex "special characters". that is because they are escaped with a \.
the only thing left to explain is the flags [NC] and [R=302,L]
NC means case-insensitive. L means last, i.e. if there is a match, then subsequent rules will not be processed. 'R' means "redirect" and 302 is the redirect's HTTP status code.
the difference between an internal and external redirect is relevant here. with an internal redirect, the server will silently grab the resources from the filepath specified at the newly formed URL, while the user still sees the original URL in the browser. the R flag indicates an external redirect and this causes the user to initiate a new HTTP transaction with the newly formed URL. Omit R for an internal redirect.
Related
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.
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]
I've got urls like this one:
my-party/viewparty/243-party-in-berlin-2013.html
where the variable part of the url is:
243-party-in-berlin-2013.html
I neet to get a url like:
events/party-in-berlin-2013.html
how can I do it with the apache rewrite rule? I've managed to remove the "my-party/viewparty" but I can't remove the portion of variable link.
Thank you very much
Alessandro
The following will map any URI in the format:
my-party/viewparty/<digits>-<rest-of-file-name>.html
to
/events/<rest-of-file-name>.html
RewriteEngine On
RewriteCond %{REQUEST_URI} my-party/viewparty/\d+-([^\.]*\.html)$
RewriteRule .* /events/%1 [L]
The way it works, is that it captures (what's inside the parentheses) the last part (denoted by the dollar-sign) of the request uri, that ends with .html and is preceded by my-party/viewparty/\d+-, where \d+ denotes a string of digits, with a minimum length of one digit. It then appends this captured pattern (denoted by %1) to /events/ in the RewriteRule.
If you want to redirect (301 Moved Permanently) to the new URI, use:
RewriteEngine On
RewriteCond %{REQUEST_URI} my-party/viewparty/\d+-([^\.]*\.html)$
RewriteRule .* /events/%1 [R=301,L]
And, if the party-in-berlin-2013 part is not variable, then you could can simply replace the RewriteCond with this:
RewriteCond %{REQUEST_URI} my-party/viewparty/\d+-(party-in-berlin-2013\.html)$
My htaccess is located in localhost/me/.htaccess, and I want to append anything after the last / with .php
e.g. localhost/me/test/index rewrites to localhost/me/test/index.php
So far RewriteRule ^([A-Za-z]+)$ $1.php [NC,L] works for localhost/me/index, but I can't get it working for the first example up there. Why doesn't ^/([A-Za-z]+)$ /$1.php [NC,L] work, and how do I change it to work?
Use this rule:
# add .php file extension
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^(.+)$ $1.php [L,QSA]
It will check if such .php exist before rewriting
The main problem you had is your pattern: ^([A-Za-z]+)$ it will match index but not test/index as it only allows a-z characters. You would need to add / into a pattern: ^([a-z/]+)$.
because you have [NC] flag (no case matching), there is no need to have both A-Z and a-z
I'm using more global/general pattern (.+)-- it will match any characters and because it comes with "check if file exist" conditions, there is no need to worry about limited set of characters.
It doesn't work because you're matching only on alpha letters and you don't have a / in the character class, but your URI is me/test/index . Try this:
RewriteEngine On
RewriteRule ^([A-Za-z/]+)$ $1.php [NC,L,QSA]
Also, since you're using [NC], you really only need a-z rather than A-Za-z but it doesn't hurt anything.
I am trying to use the .htaccess code to have different pages loaded when mobile user-agent recognized.
It doesn't work but I can't figure why, can someone please help?
The below RewriteConds work for me when followed by the RewriteRule for redirecting to subdomain, so I guess there must be a problem with my RewriteRules.
Help appreciated.
RewriteCond %{HTTP_USER_AGENT} android|avantgo|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge\ |maemo|midp|mmp|opera\ m(ob|in)i|palm(\ os)?|phone|p(ixi|re)\/|plucker|pocket|psp|symbian|treo|up\.(browser|link)|vodafone|wap|windows\ (ce|phone)|xda|xiino [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^(1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a\ wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r\ |s\ )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1\ u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp(\ i|ip)|hs\-c|ht(c(\-|\ |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac(\ |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt(\ |\/)|klon|kpt\ |kwc\-|kyo(c|k)|le(no|xi)|lg(\ g|\/(k|l|u)|50|54|e\-|e\/|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-|\ |o|v)|zz)|mt(50|p1|v\ )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v\ )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-|\ )|webc|whit|wi(g\ |nc|nw)|wmlb|wonu|x700|xda(\-|2|g)|yas\-|your|zeto|zte\-) [NC]
RewriteRule ^regular.css$ mobi.css
RewriteRule ^view.$ mobile.view.
You writing ^view.$ supposes that you think of a file named "view." (it's the complete filename, ending with a dot). Is that really what you mean?
$ marks the end (of the original string the regex is being matched against). Perhaps, you don't need it.
(Read docs about regexes before you use them in order to understand every symbol you write! Writing code and not understanding what it means is not nice.)
Further problems
I see, after you get rid of the $, you might get further problems, because you might want to construct a complete filename on the right-hand side of the rewrite-rule (like something that eveluates to "mobile.view.mainlayout.php") (or not?).
I don't remember: does Apache's rewrite-rules rewrite only the small matched piece ("view.") in the string and concatenate it with the rest of the requested filename ("mainlayout.php"), or Apache throws away the old requested filename ("view.mainlayout.php") and replaces it with what it finds on the right-hand side of the rewrite-rule (so it must be not a replacing piece like "mobile.view.", but rather evaluate to a complete filename)?
If so, then match the rest of the string with ( ) in the regex on the left-hand side of the rewrite-rule, and insert the matched piece back on the right-hand side.
Main problem is that you cannot have multiple RewriteRules after RewriteConds, so your second RewriteRule will be executed every time.
You have to do a little workaround with the skip flag, see below.
Note: The RewriteConds are 'inverted'.
The second problem is your RegEx: RewriteRule ^view.$ mobile.view. just rewrites the URL view (followed by one random char) to the URL mobile.view..
As I noticed in your comment, you have to do something like this:
RewriteRule ^view\.(.*)$ mobile.view.$1 # files and .htaccess have to be in the same directory
Here the full Rewrite code:
RewriteCond %{HTTP_USER_AGENT} !android|avantgo|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge\ |maemo|midp|mmp|opera\ m(ob|in)i|palm(\ os)?|phone|p(ixi|re)\/|plucker|pocket|psp|symbian|treo|up\.(browser|link)|vodafone|wap|windows\ (ce|phone)|xda|xiino [NC]
RewriteCond %{HTTP_USER_AGENT} !^(1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a\ wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r\ |s\ )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1\ u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp(\ i|ip)|hs\-c|ht(c(\-|\ |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac(\ |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt(\ |\/)|klon|kpt\ |kwc\-|kyo(c|k)|le(no|xi)|lg(\ g|\/(k|l|u)|50|54|e\-|e\/|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-|\ |o|v)|zz)|mt(50|p1|v\ )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v\ )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-|\ )|webc|whit|wi(g\ |nc|nw)|wmlb|wonu|x700|xda(\-|2|g)|yas\-|your|zeto|zte\-) [NC]
RewriteRule .* - [S=2] # skip next two RewriteRules if RewriteConds matched (= it's a desktop browser)
RewriteRule ^regular.css$ mobi.css
RewriteRule ^view\.(.*)$ mobile.view.$1
#Freelancer [Edited to fix errors and provide alternative]
You want a [PT] on your RewriteRule statements.
Skipping is probably your best bet:
RewriteCond "%{HTTP_USER_AGENT}" "!(first-condition-regex)" [NC]
RewriteCond "%{HTTP_USER_AGENT}" "!(second-condition-regex)" [NC]
RewriteRule "^" "-" [SKIP=2]
RewriteRule "^regular.css$" "mobi.css" [PT]
RewriteRule "^(view\..*)" "mobile.$1" [PT]
If you want to avoid inverting your conditions it gets a bit messier:
RewriteCond "%{HTTP_USER_AGENT}" "first-condition-regex" [OR,NC]
RewriteCond "%{HTTP_USER_AGENT}" "second-condition-regex" [NC]
RewriteRule "^" "-" [E=MOBILE:TRUE]
RewriteCond "%{ENV:MOBILE}" "!=TRUE"
RewriteRule "^" "-" [SKIP=2]
RewriteRule "^regular.css$" "mobi.css" [PT]
RewriteRule "^(view\..*)" "mobile.$1" [PT]
As pointed out, all these files need to be in the same directory as the .htaccess file.