How can I improve my .htaccess mod_rewrite stuff? - regex

I've created the following .htaccess file after hours of work,
Everything seems to be working properly, however I'm new to mod_rewrite, and I think my code is amateurish, so I'm looking for things to improve.
For example I thought if I use [L] at the end of a rule, the rest of rewrites will be ignored, but looking at the rewrite logs I see that they are not, there are multiple unwanted pattern matchings that certainly will slow everything down.
Also I have a book that says [C] will chain rewrite conditions, but my apache throws
http://pastebin.com/62JyBXdS

The [L] flag does indeed prevent further rules from processing, however the rewritten url could be passed back through all of your rules a second time hence the multiple entries in your log - see the manual page http://httpd.apache.org/docs/2.2/rewrite/flags.html#flag_l
Alot of your rewrite rules do the same thing with just different data and could be compacted down to a single regex, I've done a few but you could do the entire list.
RewriteRule ^/([dprcmlfb]|members|lnli|freelisting)/(.*)$ /$1\.php/$2 [L]
if you also add a RewriteCond of somethine like
RewriteCond %{REQUEST_URI} !^/[^/]+\.php
to prevent the rule firing for a php file request

You could add the MultiViews option instead of rules like the rule below:
RewriteRule ^/d/(.*)$ /d\.php/$1 [L]
MultiViews would correctly interpret /d/stuff as a request to d.php if no other rule interferes.

Related

My apache rewrite only works for the first folder level

We have a website where we show clients creative work we have produced for them. We upload raw assets to a path like this:
x.com/clients/clientName/campaignName/size/
I have a PHP script which adds our branding, contact information and other information and pulls in the raw creative (usually a swf object). It is in this directory x.com/clients/index.php and it accepts a query string parameter ?path so it knows where to look for the creative.
I am trying to do an apache rewrite in .htaccess so that our designers can upload directly to the known folder structure but so that when you go to x.com/clients/clientName/campaignName/size/ it should rewrite to x.com/clients/index.php?path=clientName/campaignName/size/
I am currently using the following rewrite rule, which works for the first folder level e.g. x.com/clients/clientName/ does successfully rewrite, but any subsequent folders do not.
RewriteRule ^clients/([^/\.]+)/?$ /clients/index.php?path=$1 [L]
My RegEx's are terrible, so I'm stuck on what to do. Any help appreciated, thank you kindly.
Your regex is only matching urls like clients/xxxxxx/ because your pattern [^/\.]+ means one or many characters except "/" or "."
With your rule, it can't work for other subdirectories.
You can change your rule by this one
RewriteRule ^clients/(.+)$ /clients/index.php?path=$1 [L]
To avoid internal server error (code 500 which means an infinite loop in this case), you can do it this way
RewriteRule ^clients/index\.php$ - [L]
RewriteRule ^clients/(.+)$ /clients/index.php?path=$1 [L]
Is there a special reason you want to use regex? In my opinion you can just catch everything coming after /clients:
RewriteEngine on
RewriteCond %{REQUEST_URI} !^(.*/)?index\.php$ [NC]
RewriteRule ^clients/(.*)$ /clients/index.php?path=$1 [L]
The second line is to prevents redirect loops, because the index.php is also in the folder /clients and this would cause never ending redirects.

Url rewriting - Querystring parsing

I am new to URL rewriting and getting a bit frustrated. I'm using Helicon on the server and have gotten most URLs to re-write correctly.
(I had to remove the '//'s to allow me to submit the questions, but the urls are the standard http:// version)
My last task is to get these:
http://example.com/Object/?page=1
http://example.com/Object/?page=1&pagesize=10
http://example.com/Object/?page=1&pagesize=10&backcolor=red
to
http://example.com/default.aspx?resource=Object&page=1
http://example.com/default.aspx?resource=Object&page=1&pagesize=10
http://example.com/default.aspx?resource=Object&page=1&pagesize=10#backcolor=red
Preferably I'd like one rule to handle all 3 possibilities, but if I need to make 3 rules, one for each, and add a [L] or something at the end that would be ok too. I just can't get the querystring parsing right.
Here is an existing rule I have that works to give you an idea of what I've been doing:
RewriteRule ^/([a-zA-Z0-9]+)(/([a-zA-Z0-9]+)(/([a-zA-Z0-9]+))?)?/?($|\?) /default.aspx?resource=$1&id=$3&option=$5 [L]
It's for a separate example, but the syntax shows what I'm doing.
Does this work for you?
RewriteRule ^\/([^/]*)\/\?(.*)$ /default.aspx?resource=$1&$2 [L]
[Update]
Try this:
RewriteRule ^([^/]*(?=\/)|[^?]*(?=\?)|.*)($|[^\?]*\?(.*)) /default.aspx?resource=$1&$3 [L]

.htaccess RewriteRule for multiple pages

I've done RewriteRule for index page in my website.
Here is the code below:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^([a-zA-Z0-9+\-\(\)]+)/?$ /index.php?origin=$1
RewriteRule ^([a-zA-Z0-9+\-\(\)]+)/([a-zA-Z0-9+\-]+)/?$ /index.php?origin=$1&gender=$2
RewriteRule ^([a-zA-Z0-9+\-\(\)]+)/([a-zA-Z0-9+\-]+)/([a-zA-Z0-9+\-]+)/?$ /index.php?origin=$1&gender=$2&type=$3
RewriteRule ^([a-zA-Z0-9+\-\(\)]+)/([a-zA-Z0-9+\-]+)/([a-zA-Z0-9+\-]+)/([a-zA-Z0-9+\-]+)/?$ /index.php?origin=$1&gender=$2&type=$3&page=$4
</IfModule>
and the url is looks like
http://babynames.agurchand.com/scottish/male/pythagorean
It's a single page website, so everything is working fine so far.
Then i wanted to add one more page on my website and i have added too. file name is 'namemeaning.php'.
I've added the below code in addition of the htaccess pasted, at the bottom. But It doesn't seem to be working.
RewriteRule ^/namemeaning/([a-zA-Z0-9+\-\(\)]+)/?$ /namemeaning/namemeaning.php?name=$1
I've tried with the below url, but I'm still being referred to the index page only.
http://babynames.agurchand.com/namemeaning/abasi
Can anyone give me a solution for this please!
You're being forwarded to the index page because your regular expression for the index page fits your request. You're being sent to index.php?origin=namemeaning&gender=abasi
http://babynames.agurchand.com/namemeaning/abasi
'namemeaning' fits the rule ([a-zA-Z0-9+\-\(\)]+)
'abasi' fits the rule ([a-zA-Z0-9+\-]+)
A simple fix would be adding the line
RewriteRule ^/namemeaning/([a-zA-Z0-9+\-\(\)]+)/?$ /namemeaning/namemeaning.php?name=$1
above the line
RewriteRule ^([a-zA-Z0-9+\-\(\)]+)/?$ /index.php?origin=$1
You should remember to always place the most specific RewriteRule(s) above those that are more general.
For more information about regular expressions, I can advice you this website.
The rewruite rules are checked sequentially from top to bottom, and all rules matched are applied in that order. Double check that none of your rules match before the desired one.
Good Tool for that purpose is http://www.regextester.com/

Need to override previously written rewrite rule

I have some rewrite rules in an htaccess file. I'm still getting into it so theres a few things I'm unsure of.
Basically I want all pages (except the /register page) to be rewritten like this: http://www.example.com/about -> http://www.example.com?page=about
To get that right I wrote this rule:
RewriteRule ^([a-z-_1-9]+)+/?$ ./?page=$1&%{QUERY_STRING} [L]
I then wrote this rule below the one above thinking it would override it, but it doesnt...
RewriteRule ^register/?$ ./?page=login&option=register
So going to /register gives me a 404. However if I comment out the first rule then the register page works.
I was thinking it would work like CSS where writing a new rule below would take precedence.
How would I get this right and how do you override previously written rewrite rules?
Thanks!
.htaccess is not CSS -- especially when it comes to mod_rewrite instructions/rules.
The rules are executed from top to bottom. Therefore -- put more specific rules at top and then more generic at bottom.
In your case:
RewriteRule ^register/?$ ./?page=login&option=register [L]
RewriteRule ^([a-z-_1-9]+)+/?$ ./?page=$1&%{QUERY_STRING} [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.