Rewrite rule with pagination .htaccess - regex

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.

Related

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.

URL Regex for Apache Mod Rewrite

I have been trying to work the regex out for this for a while now and I am struggling. Was hoping someone could help.
I have a website using apache mod_rewrite converting directories into get variables to 1 level. I am wanting to change this or add a seperate rule for the following
example.com/portfolio/plugins/jquery-tester
becoming
example.com/portfolio/handler.php?area=plugins&item=jquery-tester
I am trying to currently build up on php live regex but coming up trumps.
Try this:
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteRule ^/portfolio/(.+)/(.+)$ /portfolio/handler.php?area=$1&item=$2 [L]
Having the handler in the same directory as what you're matching can cause potential problems though (that's why the RewriteCond is there to check so you don't end up with an infinite loop).
If you have other rewrite rules, you may need to check that there aren't any conflicts.
RewriteRule ^/portfolio/[a-zA-Z\-_]+/[a-zA-Z\-_]+$ /portfolio/handler.php?area=$1&item=$2 [L]

Correct .htaccess file for user-friendly URL

I supposed to have the following URLs to be converted to user-friendly format:
example.com/product/$numbers/$anychars => example.com/product.php?product_id=$numbers&name=$anychars
example.com/image/$numbers/$anychars/$number => example.com/image.php?image_id=$numbers&name=$anychars&no=$number
example.com/item/$numbers/$anychars => example.com/item.php?item_id=$numbers&name=$anychars
example.com/category/$anychars => example.com/category.php?name=$anychars
example.com/category/$anychars/$numbers => example.com/category.php?name=$anychars&page=$numbers
Trailing forward slash should be allowed and ignored by the web-server.
Using some guides from the Internet I did the following:
RewriteEngine On
RewriteRule ^product/([0-9]+)/([^/]+)/?$ product.php?id=$1&name=$2 [QSA,NC,L]
RewriteRule ^image/([0-9]+)/([^/]+)/([0-9]+)/?$ image.php?item_id=$1&item_name=$2&no=$3 [QSA,NC,L]
RewriteRule ^item/([0-9]+)/([^/]+)/?$ item.php?id=$1&name=$2 [QSA,NC,L]
RewriteRule ^category/([^/]+)/?$ category.php?cat=$1&page=0 [QSA,NC,L]
RewriteRule ^category/([^/]+)/([0-9]+)/?$ category.php?cat=$1&page=$2 [QSA,NC,L]
NC flag indicated to make it case insensitive.
L flag indicated to stop searching for match after one match was found (less bugs and faster URL handling)
Preliminary testing showed no errors.
But as regexps and mod_rewrite is not my best hobbies I'd like to ask you to check if I didn't make any errors. And if there's no - it could be a good pattern for a guys like me looking for a easy mod_rewrite solution.
There is one recommendation I would make. Put the rules in order from most specific match to most general match (in this case, switch the two category rules). By following this convention you ensure that a URL that may satisfy more than one rule is caught by the more specific rule.
In your specific case, you won't hit this problem yet, but as you grow your rules it will eventually bite you.
I haven't added QSA flags. I recommend that you create rules that would allow the user to see an entirely friendly URL, rather than a partly-friendly URL - to do this, ensure that you map additional parameters just like you have the ids and categories in your existing rules.
RewriteEngine On
RewriteRule ^product/([0-9]+)/([^/]+)/?$ product.php?id=$1&name=$2 [NC,L]
RewriteRule ^image/([0-9]+)/([^/]+)/([0-9]+)/?$ image.php?item_id=$1&item_name=$2&no=$3 [NC,L]
RewriteRule ^item/([0-9]+)/([^/]+)/?$ item.php?id=$1&name=$2 [NC,L]
RewriteRule ^category/([^/]+)/([0-9]+)/?$ category.php?cat=$1&page=$2 [NC,L]
RewriteRule ^category/([^/]+)/?$ category.php?cat=$1&page=0 [NC,L]

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.