I'm trying to understand mod_rewrite better and have one particular problem I think I need to get my head round first.
I am rewriting http://www.somesite.tld/a/b/c to index.php?path=a/b/c using the following
RewriteRule ^(?!index.php)(.*)$ index.php?path=$1 [NC,L]
An equivalent rewrite would, in this case, be
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?path=$1 [NC,L]
This does not work without the RewriteCond -- path=index.php would be the result without specifically ignoring files or saying 'not index.php'. Why is this?
Also, what is the ?! and ?: syntax that I sometimes see used? I do not understand the use of the ? when it is not prefixed by anything.
And why, in the first RewriteRule above, do the second pair of brackets return a match for $1?
Cheers
(?= ...) and (?! ...) is special syntax in Perl regular expressions and in PCRE, which is the regex library that Apache uses. They are, respectively, positive and negative lookahead assertions: they match an empty string if the text after it matches or does not match the content in the brackets.
They are non-capturing, so they don't define any $n (it would be pointless, since they match an empty string). (?: ...) is also non-capturing, it is used to group subexpressions.
Your first rule should work in .htaccess (but not in a virtual host configuration file), though it would be more correct to write it as
RewriteRule ^(?!index\.php$)(.*)$ index.php?path=$1 [L]
Perhaps another rule is interacting with it. You can check what exactly is being matched and rewritten with RewriteLog and RewriteLogLevel.
"!" means negation. Like a = 1 (a is equal one) a != 1 (a is not equal one);
"f" means file. So if you use together with "!", like "!-f" would be something "file does not exist". the links below may help you better:
http://www.askapache.com/htaccess/htaccess.html
http://net.tutsplus.com/tutorials/other/using-htaccess-files-for-pretty-urls/
http://corz.org/serv/tricks/htaccess2.php
Related
I have a problem with .htaccess file, as I understand RewriteRule help to rewrite the URL. But when I try the following 2 cases it doesn't work.
#1 The first RewriteRule works but the second doesn't work
RewriteRule ^([a-zA-Z0-9_-]+)$ index.php?idcat=$1 [L] #working
RewriteRule ^([a-zA-Z0-9_-]+)$ index.php?idl=$1 [L] #not working
#2 The RewriteRule doesn't work with dash but works with slash and underscore.
RewriteRule ^([a-zA-Z0-9_-]+)-([a-zA-Z0-9_-]+)$ index.php?idl=$1&iddis=$2 [L] #not working
RewriteRule ^([a-zA-Z0-9_-]+)_([a-zA-Z0-9_-]+)$ index.php?idl=$1&iddis=$2 [L] #working
RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)$ index.php?idl=$1&iddis=$2 [L] #working
So how to fix these problems? Does anyone have any suggestions for me?
#1 The first Rewriterule works but the second doesn't work
RewriteRule ^([a-zA-Z0-9_-]+)$ index.php?idcat=$1 [L] #working
RewriteRule ^([a-zA-Z0-9_-]+)$ index.php?idl=$1 [L] #not working
Because you are using the same pattern in both rules, the first rule always "wins" and the second rule is never triggered. This is essentially processed as follows (pseudo-code):
if (the URL matches the pattern "^([a-zA-Z0-9_-]+)$") {
rewrite the request to "index.php?idcat=<url>"
}
elseif (the URL matches the pattern "^([a-zA-Z0-9_-]+)$") {
rewrite the request to "index.php?idl=<url>"
}
As you can see, the second code block is never processed since the expressions are the same.
To put it another way, how would you determine whether a request of the form /foo should be rewritten to index.php?idcat=foo or to index.php?idl=foo? You can't rewrite the request to both.
In this particular case you could perhaps rewrite everything to index.php?id=<url> and let your script decide whether it should be idcat or idl. Otherwise, there needs to be something different about the two URLs (and consequently the patterns you are using to match the URLs) that allows you to determine how the URL should be rewritten.
#2 The Rewriterule doesn't work with dash but works with slash and underscore.
RewriteRule ^([a-zA-Z0-9_-]+)-([a-zA-Z0-9_-]+)$ index.php?idl=$1&iddis=$2 [L] #not working
RewriteRule ^([a-zA-Z0-9_-]+)_([a-zA-Z0-9_-]+)$ index.php?idl=$1&iddis=$2 [L] #working
Both these rules have the same problem, depending on the URLs being requested. This is because the patterns/regex you are using are "ambiguous". Each of the two subpatterns (either side of the delimiter), that are used to match the idl and iddis values, contain the same character as the expected delimiter, - or _. However, in the 3rd rule (not shown), you are using a / as the delimiter, which does not occur in the surrounding subpatterns, so there is no ambiguity,
For example, how should (or you would expect) a URL of the form /foo-bar-baz to be matched by the first rule? Since the first subpattern uses the greedy quantifier +, it will capture foo-bar and baz and rewrite the request to index.php?idl=foo-bar&iddis=baz.
To avoid this "ambiguity" you need to make sure the delimiter between the subpatterns (ie. between the values for idl and iddis) is different to the characters used in the subpatterns (or at least one of the two subpatterns).
This can often be resolved by making the regex as specific as possible. ie. Match only the valid characters in idl and iddis.
To begin resolving this issue, you need to first identify the precise URLs you are trying to match, before implementing the rules to match them.
I am doing a rewriterule inside of my .htacess folder in one of my htdocs folders.
The rewriterule looks something like this:
RewriteRule ^index/(blah)/(blah2)/(blah3)..../(blah20)
^^^The above code looks like bad practice--don't worry about that.
Anyways, I heard before that ${20} was the correct way to access the 20th match group in regex, but even though in regex101 my 20th match group is matching blah20, whenever I print out the 20th capture group, I just get ${20}.
Why is this? Am I correctly accessing two digit match groups?
Edit--real rewriterule:
RewriteRule ^a/([\d]*)/(b/([\d]{2}:[\d]{2}:[\d]{2})/?)?(c/(\w*)/?)?(d/([\w]
{6})/?)?(e/([\w]{6})/?)?(f/([\w]{6})/?)?(g/([\w]{6})/?)?(h/([\w]{6})/?)?
(i/([\w]{6})/?)?(j/([\w]{6})/?)?(k/([\w]{6})/?)?(l/([\w]{6})/?)?(m/([\w]
{6})/?)? /index.php?a=$1&b=$3&c=$5&d=$7&e=$9&f=${11}&g=${13}&h=${15}&i=${17}&
j=${19}&k=${21}&l=${23}&m=${25} [L]
You cannot use back-reference number greater than 9 as per official mod_rewrite documentation.
From Manual:
RewriteRule back-references: These are back-references of the form $N (0 <= N <= 9). $1 to $9 provide access to the grouped parts (in parentheses) of the pattern, from the RewriteRule which is subject to the current set of RewriteCond conditions. $0 provides access to the whole string matched by that pattern.
If you are dealing with so many back-references then it is better to pass full URI after index/ to index.php and use explode inside the php code:
RewriteRule ^index/(.+)$ index.php?q=$1 [L,QSA,NC]
For example like this:
RewriteRule \^index(?:\/\w+){5}\/(blah6)
Will match 6th folder in the url.
I have declare Rewrite rule if url start with 'katalogas/imone' or 'imone' I try that:
RewriteRule ^(katalogas/imone|imone)/(.*) http://google.lt
In statment (katalogas/imone|imone) is something wrong, because '/' means that 2 argument is not important, how to escape '/'. I mean it must start 'katalogas/imone' or 'imone'.
I don't see a problem with your regular expression. The only optimization could be to refactor the two prefixes into one with an optional part
RewriteRule ^((?:katalogas/)?imone)/(.*) http://google.lt
and since you don't use the captured parts, you can further simplify it to
RewriteRule ^(?:katalogas/)?imone/ http://google.lt
Another approach could be to just use two separate rules
RewriteRule ^katalogas/imone/ http://google.lt
RewriteRule ^imone/ http://google.lt
I'm almost there with a mod_rewrite rule, but I've caved in :)
I need to rewrite
country/[countryname].php
to
country/[countryname]/
however, [countryname] may have an underscore like this: 'south_africa.php' and if it does I want to replace it with a hypen: 'south-africa/'
I also want to match if the country has numbers following it: 'france03.php' to 'france/'
Heres my rule, its almost there but its still adding a hyphen even if there is no second part after the underscore.
RewriteRule ^country/(.*)_(.*?)[0-9]*\.php$ country/$1-$2 [R=301,L]
so currently 'country/south_.php' becomes 'country/south-/'
Can someone please help me find the missing piece of the puzzle? Thanks.
Try this:
RewriteRule ^country/([^_]*)_([^_]*?)\d*\.php$ country/$1-$2 [R=301,L]
This rule will match urls with a single underscore - you'll need a different rule for more underscores or none.
If you want to make sure $2 contains only letter and isn't empty, change ([^_]*?) it to ([a-zA-Z]+).
Alternatively you could do it over several passes:
# If request is for something in "country/"
RewriteCond %{REQUEST_URI} ^country/.+\.php$
# Replace underscore and digits with (single) hyphen
RewriteRule [_0-9]+ \-
# Remove extension (and possible trailing hyphen)
RewriteRule ^(.*)-?\.php$ $1
# Final rewrite
RewriteRule ^country/(.*)$ country/$1 [R=301,L]
Untested ... and not necessarily "pretty" :)
I have to do a redirect to another host if a certain parameter/value pair is in the querystring.
So far I have
RewriteCond %{REQUEST_URI}?%{QUERY_STRING} [&\?]abc=23&?
RewriteRule ^(.*)$ http://anotherserver.com/$1 [R,NC,L]
that works for:
/index.php?id=95&abc=23
/index.php?abc=23&id=95
/index.php?id=95&abc=23&bla=123
but it also matches /index.php?id=95&abc=234 for example.
I need a pattern that matches exactly abc=23, no matter where it occurs.
Any suggestions on this? :-)
I'd try this regex (&|^)abc=23(&|$) and match is only against %{QUERY_STRING}.
The question mark makes the preceding token in the regular expression optional. E.g.: colou?r matches colour or color.
RewriteCond %{REQUEST_URI}?%{QUERY_STRING} [&\?]abc=23&?
You are matching abc=23& OR abc=23 with the rest of the string unconstrained so abc=234 is a valid match. What you really want is & or nothing else. I'm not sure if this RegExp is legal in Apache but it would be written as:
RewriteCond %{REQUEST_URI}?%{QUERY_STRING} [&\?]abc=23(&|$)
Here are the test cases I used at my favourite online RegExp tester:
abc=23&def=123
abc=234
abc=23