RewriteRule/RewriteCond help - match anything except a directory - regex

Currently, I have the rule
RewriteRule ^([A-Za-z0-9-/]+)$ myFile.php?myId=$1
I want to be able to access a folder and its files (lets just say it's called example), and the index.php of it. However, using the rule
RewriteRule ^example$ example/index.php [L]
it instead matches the first rule. It is the first rule of the file, though.

After your rewrite rules are executed, the example URL is rewritten to example/index.php, and then re-enters the rewrite process (the [L] flag does not prevent that). Then, your other rewrite rule matches.
So you must add an exception for example/index.php. For example:
RewriteRule ^example/index.php$ - [L]
which terminates the second rewrite run. Make sure to insert it above your other rule.

You could use a condition:
RewriteCond %{REQUEST_URI} !^/example
RewriteRule ^([A-Za-z0-9-/]+)$ myFile.php?myId=$1 [L]
This way you will get your rule match, unless request begins with '/example'. You may add extra slash, i.e. '/example/' to avoid un-matching '/example-page'.

Related

Rewrite rule with pagination .htaccess

I have a URL like this:
http://example.com/category/title which comes from the link http://example.com/cview.php?url=title
I want to create pagination and to be like http://example.com/category/title/page/1 or
http://example.com/category/title/1
this comes from http://example.com/cview.php?url=title&pageno=1.
I have tried this in .htaccess without success
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^category/([^/]*)$/([^/]+)/?$ /cview.php?url=$2&pageno=$1 [L]
Can anyone help please?
RewriteRule ^category/([^/]*)$/([^/]+)/?$ /cview.php?url=$2&pageno=$1 [L]
You have an erroneous $ (end-of-string anchor) in the middle of the RewriteRule pattern. You also appear to have the backreferences $1 and $2 the wrong way round. You are also allowing an optional trailing slash, yet your example URLs do not use this. (An optional trailing slash potentially creates a duplicate content issue.)
If you allow both /category/title/page/1 and /category/title/1 then you are potentially creating a duplicate content issue. Presumably you are only linking to one of these URL formats?
Since the page number is a "number" then it makes sense to just match numbers, rather than anything - this also helps to avoid conflicts with other directives.
It doesn't look like you need the conditions (RewriteCond directives) that check the request does not map to a file or directory, since I wouldn't expect a request of the form /category/title/page/1 to map to a file or directory anyway?
Try the following instead (without the RewriteCond directives):
RewriteRule ^category/([^/]+)(?:/page)?/(\d+)$ /cview.php?url=$1&pageno=$2 [L]
This matches both /category/title/page/<num> and /category/title/<num>. The optional subpattern (?:/page) is non-capturing, so that it doesn't mess up the numbering of the backreferences.
Bear in mind also that the order of the rules in .htaccess is important in order to avoid conflicts.

RewriteCond REQUEST_URI not match whole path

I am quite puzzled.
My goal is to detect, whether redirect is needed (path changed). This is a minimal example.
RewriteRule ^first$ second
RewriteCond %{REQUEST_URI} !^/$1$
RewriteRule ^(.*)$ /$1 [R=301,L]
And I am requesting example.com/first with intention to get 301 to second.
Problem is, that the RewriteCond always evaluates to true and creates a loop.
On the first go, it is fine. But on the second request, which is now example.com/second, it evaluates to true again, even though %{REQUEST_URI} is /second and $1 is second. I know it is.. I checked by redirecting to URL with both variables appended.
Any idea what am I missing?
Please remember 2 important facts here:
mod_rewrite rules are run in a loop and it stops only when there is no successful rule execution
Value of %{REQUEST_URI} changes after rewrite or redirect.
Looking at your rules your 2nd redirect rule is faulty since you cannot use %1 or $1 in value part of RewriteCond thus making it always return true due to negation.

Why does this rewrite rule result in an infinite loop?

I want to serve files matching a certain pattern from a subdirectory but my rule results in infinite redirect loop. In this example I want to serve google site verification files from a new path:
RewriteRule ^(google.*html)$ /google_site_verification/$1 [L]
According to my error log this results in an internal redirect loop which keeps adding /google_site_verification to the path. I have also tried:
RewriteCond %{REQUEST_URI} ^/google.*html$
RewriteRule ^(.*)$ /google_site_verification/$1 [L]
Which gives the same result. Since my regex explicitly defines beginning and ending of the pattern, why does /google_site_verification/googleabcd1234.html match? The only thing I've tried that works is adding
RewriteCond %{REQUEST_FILENAME} !-f
into the chain, but I don't want to rely on the file not existing for things to work.
You can use:
RewriteRule ^(google[^/]*\.html)$ /google_site_verification/$1 [L]
Your problem is that both urls match:
/google.html
/google_site_verification/google.html

URL rewrite to exclude certain HTML files

I need to URL write uppercase URLs to lowercase.
I got that part working.
However, I am having problem. I am interested in excluding from the above *.html?vs=12312312
I tried the following:
RewriteCond %{REQUEST_URI} !^.*\.(html\?vs=) [NC]
But it didn't work.
http://foo.com/com.foo.bar/content/Any.html?vs=12312312
The above should stay the way it is, and not be rewritten.
What's wrong with the rule above? What should be the proper syntax?
Update
I tried the following:
RewriteCond %{REQUEST_URI} !(.*\.html\?vs=.*)$ [NC]
But still no luck.
The query string is not part of the REQUEST_URI, it is stored in QUERY_STRING. So try something like this, which goes before your existing rule:
RewriteCond %{REQUEST_URI} \.html$
RewriteCond %{QUERY_STRING} ^vs=[^&=]+$
RewriteRule ^ - [L]
The reason you need to do it this way (as its own separate rule), rather than putting an exclusion on your existing rule, is because you can't do AND with negative matches of RewriteCond, so putting them on your existing rule as negative matches would prevent it from running if only one applied (.html or ?vs=nnn). To reject when both apply, you need to do it in a separate, positive match like this.
If you have other rules you need to apply to those URLs after this, look at the [S=1] flag (documentation) which will skip the next rule on a match, instead of [L] which says stop processing here after a match (and hence don't apply your subsequent rules for these URLs).
The rule RewriteRule ^ - just says don't change anything, it's used to only apply the effect of the flags.

Is there a better way to do this regex?

I finally figured out a good/easy way to make clean URLs with regex on my site in this format below, however it will require a very large .htaccess file, I know from the post on here that it is supposed to not be to bad on performance to use mod_rewrite but I have never really seen it used where the way I am, with a seperate entry for almost every page of my site.
Below is an example of an entry, there is 2 entries for 1 page, the first entry re-writes
http://www.example.com/users/online/friends/
to
http://www.example.com/index.php?p=users.online.friends
It works great but if the user is not on the first page then there is another thing added to the URL for paging and I had to write another entry to rewrite when this happens, is this the correct way or should these be combined somehow?
RewriteRule ^users/online/friends/*$ ./index.php?p=users.online.friends&s=8
RewriteRule ^users/online/friends/(\d+)/*$ ./index.php?p=users.online.friends&s=8&page=$1
The second one would do this
http://www.example.com/users/online/friends/22/
to
http://www.example.com/index.php?p=users.online.friends&page=22
It depends what you think is more readable, but here's how you could do it with a single rule:
RewriteRule ^users/online/friends(/(\d+))?/*$ ./index.php?p=users.online.friends&s=8&page=$2
(Edited to be more faithful to treatment of trailing slash in original question. Was: RewriteRule ^users/online/friends/((\d+)/*)?$ ./index.php?p=users.online.friends&s=8&page=$2)
Here I've just put "(...)?" around the final part of the url to make it an optional match, and changed the backreference to $2.
Of course, this actually rewrites http://www.domain.com/users/online/friends/ as:
http://www.domain.com/index.php?p=users.online.friends&page=
So your PHP code would have to check whether the page parameter is non-empty.
Yes, that's fine. I guess they could be combined into a single rule but there's not really any need.
You might consider leaving page as part of the URL so instead of:
http://www.domain.com/users/online/friends/22/
just have:
http://www.domain.com/users/online/friends?page=22
and then have one rule something like:
RewriteRule ^users/online/friends/?$ ./index.php?p=users.online.friends&s=8 [L,QSA]
to append the query string
Edit: There are a couple of ways of reducing the number of rewrite rules you have.
Firstly, use wildcards in the search terms, like:
RewriteRule ^users/(\w+)/(\w+)$ /index.php?p=users.$1.$2 [L,QSA]
will reduce quite a number of rules.
Secondly, if you're passing everything through /index.php just consider delegating all requests there:
RewriteRule ^(users/*)$ /index.php/$1 [L,QSA]
That rule uses a third technique: instead of passing the path information via a query string parameter, pass it via the extra path info. That can be accessed via $_SERVER['PATH_INFO'].
That being said, lots of rules isn't necessarily bad. At least it's explicit about all your actions. The thing you have to watch out for is creating a maintenance nightmare however.
# Initial step
RewriteCond %{QUERY_STRING} !(?:^|&)p=
RewriteRule ^([^/]+)/(.+) /$2?p=$1 [QSA]
# Subsequent steps
RewriteCond %{QUERY_STRING} ((?:[^&]*&)*?)p=([^&]*)(.*)
RewriteRule ^([^/]+)/(.+) /$2?%1p=%2.$1%3
# Last step with page number
RewriteRule ^(\d+)/?$ /index.php?page=$1 [QSA,L]
# Last step without page number
RewriteCond %{QUERY_STRING} (?:((?:[^&]*&)*?)p=([^&]*))?(.*)
RewriteRule ^([^/]+)/?$ /index.php?%1p=%2.$1%3 [L]
This would rewrite the URL in several steps:
http://www.domain.com/users/online/friends/22/
http://www.domain.com/online/friends/22/?p=users
http://www.domain.com/friends/22/?p=users.online
http://www.domain.com/22/?p=users.online.friends
http://www.domain.com/index.php?p=users.online.friends&page=22
An easier method would be the following, but would require you to change your scripts:
RewriteRule ^(.*?)(?:/(\d+))?/?$ /index.php?p=$1&page=$2 [QSA,L]
It would do everything in one step, with a little difference:
http://www.domain.com/users/online/friends/22/
http://www.domain.com/index.php?p=users/online/friends&page=22
Adding the s=8 query argument would require more work:
Creating a text-file with the menu numbers for each page.
Adding a RewriteMap directive.
Changing the second-last rule to use the same RewriteCond as the last rule has.
Adding &s=%{menumap:%2|0} to the last two rules.