Forum Moderators: phranque
Looks like this:
RewriteCond %{REQUEST_URI}!\.
RewriteRule (.*)/(.*)/(.*)/(.*) $1.php?id=$3&page=$4&name=$2 [L]
I need to add to it that when the url looks like this:
www.domain.com/john_smith
It will load: name_list.php?name=john_smith
1) How can I do that? (it is given that john_smith is unique name)
2) I still need urls like www.domain.com/admin to provide me with admin/index.php
The problem here is that there is no way to uniquely-identify the first URL-path component. That is, how can you tell if it is a user-name or a php file-name? Without adding something to the requested URL, you can't.
You could use
www.domain.com/user/john_smith
or you could add something to the page URLs
www.domain.com/pages/page_name/name/id/page_num
Then you could have mod_rewrite look for "user" or "pages" as the first URL-path component.
Another way to do it would be to set up wild-card DNS, and then let your users have their own unique subdomains. like john_smith.domain.com.
These methods only work well for small limited-access sites, though, because with big, public you will end up with two John Smiths eventually, and then what do you do?
Anyway, the key to your posted question is that you have to give mod_rewrite something unique in the URL-path, so that it can tell username URLs from php-filename URLs.
P.S. You can speed up your existing code by using "([^/]+)" or "([^/]*)" instead of "(.*)" for $1 through $3.
Jim
I see your point…
Two questions:
1- Why is "([^/]+)" faster than (.*)?
2- If I wanted to combine my existing rule:
RewriteCond %{REQUEST_URI}!\.
RewriteRule (.*)/(.*)/(.*)/(.*) $1.php?id=$3&page=$4&name=$2 [L]
With a rule that will rewrite www.domain.com/members/john_smith
to: members.php?name=john_smith
(it will apply only when the first dir name is ‘members’. So something like: if $1=’members then rewrite to members.php?name=$2).
can that be done at all?
Thanks again
".*" is a non-specific and "greedy" expressions; It will match as many characters as possible in the input string. Therefore, the first ".*" will match the whole string. Then the regex parser will see that you require more matching *after* the first ".*", so it will "back off" on the match and try again, leaving one character (the last slash) unmatched at the end of the string, in order to satisfy $2.
But then, $3 and $4 won't match, so it starts backing off some more. This repeats again for $3 and $4 and is highly inefficient, because of the high number of iterations needed to find just the right way to make the match -- You can visualize this as regex being forced to attempt to match from right to left in the string, instead of matching from left to right.
By contrast, using a pattern of "[^/]*", meaning, "accept any number of characters up to the first '/' you find," is a straightforward left-to-right match, and so is much faster. Using "[^/]+" makes more sense in this case, as long as there is always at least one character between slashes.
2- If I wanted to combine my existing rule:RewriteCond %{REQUEST_URI}!\.
RewriteRule (.*)/(.*)/(.*)/(.*) $1.php?id=$3&page=$4&name=$2 [L]With a rule that will rewrite www.domain.com/members/john_smith
to: members.php?name=john_smith
(it will apply only when the first dir name is ‘members’. So something like: if $1=’members then rewrite to members.php?name=$2). Can that be done at all?
Of course, you can stack rules several hundred deep if you need to. Just put them in order from most-specific to least-specific, and use the [L] flag.
RewriteRule ^members/(.+)/?$ members.php?&name=$1 [L]
RewriteRule ^([^/]+)/([^/]+)/([^/]+)/(.*) $1.php?id=$3&page=$4&name=$2 [L]
Jim
Still couldn't get it to work:
1) The original version:
RewriteEngine on
RewriteCond %{REQUEST_URI}!\.
RewriteRule (.*)/(.*)/(.*)/(.*) $1.php?id=$3&page=$4&name=$2 [L]
works fine.
2)
RewriteEngine on
RewriteCond %{REQUEST_URI}!\.
RewriteRule ^([^/]+)/([^/]+)/([^/]+)/(.*) $1.php?id=$3&page=$4&name=$2 [L]
#RewriteRule (.*)/(.*)/(.*)/(.*) $1.php?id=$3&page=$4&name=$2 [L]
does not work: i get "The requested URL /members/name/id/page was not found on this server".
3)
RewriteEngine on
RewriteCond %{REQUEST_URI}!\.
RewriteRule ^members/(.+)/?$ members.php?&name=$1 [L]
#RewriteRule ^([^/]+)/([^/]+)/([^/]+)/(.*) $1.php?id=$3&page=$4&name=$2 [L]
#RewriteRule (.*)/(.*)/(.*)/(.*) $1.php?id=$3&page=$4&name=$2 [L]
works when i put in a url like /members/name/id/page , but what i wanted it to do is do this rewrite only
when the url is like:/members/donna. only then i want it to be rewritten to members.php?name=$1.
What can i change? do I need a seperate condition for that?
Yes.
> What can i change?
The first step is to get a well-defined plan. Can you provide a complete map of possible input and output URLs, and mark the variable parts (the parts which need to be back-referenced) clearly? The first step is to get a well-defined plan. Examples of input URLs that *should not* be rewritten are needed, as well as URLs that need to be rewritten. Communicating these requirements is always a challenge, but it is necessary.
One very important question: Is this code for use in httpd.conf or in .htaccess?
Jim
Whenever I try to use the expression ([^/]+) in ANY context - WHATSOEVER - in my .htaccess file, I get an internal server error. I have no idea why, it's a perfectly valid regex that I have used tons of times elsewhere.. my Apache server seems to hate it though.
I'll try to find a way around it because I'm addressing this exact issue too.
The structure:
(.*)/(.*)/(.*)/(.*)
REQUIRES at least 3 levels of faux directories - i.e., those slashes are required parts of the pattern.
How can I make that pattern match ANY pattern up to and including all 4 sections?
I.e. a pattern that will match BOTH
/page/
AND
/page/num/section/
I have tried using something like:
RewriteRule ^(.*)/([^/]+)?/?(.+)/?$ $1.php?id=$2&gal=$3 [L]
to make the slashes OPTIONAL, but it doesn't work...
(BTW got the forward-looking slash to work as you can see).
RewriteRule ^([^/]+)/$ $1.php [L]
RewriteRule ^([^/]+)/([^/]+)/([^/]+)/(.*) $1.php?id=$2&gal=$3&page=$4 [L]
This now works for:
/top/
AND
/galleries/name/num/
BUT does not work for regular pages like:
/galleries/name/
So it's requiring either 1 or 3 levels, but won't allow 2.
I can see why (first rule has one replacement, second rule has 3) but I can't figure out how to fix it.
Just made the last sections optional using? as such:
RewriteRule ^([^/]+)/$ $1.php [L]
RewriteRule ^([^/]+)/([^/]+)/([^/]+)?/?([^/]+)? $1.php?id=$2&gal=$3&page=$4 [L]
Viola. Seems to work like a charm. I can remap anything from:
/file
through and including
/file/id/name/page/
(i.e., I can do
/file
/file/id/
/file/id/name/
/file/id/name/page/
)
Without problems...
Which also addresses the first poster's problem, needing to map something like
/username
to name_list.php?name=username
My solution is just to make the page use a 301 redirect to add a slash to the end of the URI.
This is my full htaccess file:
RewriteEngine on# Set rewrite base to the base directory
RewriteBase /# If the url is an existing directory
RewriteCond %{REQUEST_FILENAME} -d# Don't rewrite the url
RewriteRule ^.*$ - [L]# If the URL does not end with a slash
RewriteCond %{REQUEST_URI}!/$# and the URL does not contain a full stop (Example: index.html)
RewriteCond %{REQUEST_URI}!\.# Rewrite the url to have a trailing slash
RewriteRule (.+) /$1/ [R=301,L]# If the url isn't an image or the css file
RewriteCond %{REQUEST_URI}![a-zA-Z0-9]\.gif¦jpg¦css$# Rewrite the url to the dynamic query string
RewriteRule ^([^/]+)/$ $1.php [L]
RewriteRule ^([^/]+)/([^/]+)/([^/]+)?/?([^/]+)? $1.php?id=$2&gal=$3&page=$4 [L]
If anyone has any optimization suggestions I'm all ears.
Edit:
Make sure to change the broken vertical bar into the real one.
One last edit:
One might complain that now I ALWAYS have the gal and page variables in the query string, even if they're not used - which is true.. but they are of no effect except on the pages that use them, so they don't change the behavior of any page that doesn't use them.
On the pages that use them, the input is checked for security anyway, so there's no additional problems caused by them always being in the URL.
If anyone has any suggestions on how to ONLY add them in the URL when they're needed I'd love to hear it. I don't think regex is that flexible though without some kind of scripting language like PERL or PHP.
Welcome to WebmasterWorld [webmasterworld.com]!
It is possible that old versions of Apache may not support extended regular expressions, so check your version.
The trick to handling requests with optional parameters is to use multiple RewriteRules, with each rule handling one less parameter than the one that precedes it. This works only if the parameters are always in the same order in the requested URL. If your parameters are not in fixed order, see this thread [webmasterworld.com] for a solution.
There is no need to do a redirect for a missing trailing slash. Simply follow the slash in your pattern with a "?" so the pattern matches with or without it.
Jim
There is no need to do a redirect for a missing trailing slash. Simply follow the slash in your pattern with a "?" so the pattern matches with or without it.
The problem with doing it this way that I ran into is that a URL like
[example.com...]
Doesn't get interpreted by mod_rewrite. Some superior set of settings, probably in httpd.conf, takes over the parsing of that particular URI, and no matter what I did I couldn't force mod_rewrite to notice it and do something with it. The only thing I was able to do was get it to add a slash to the URI. So that's the solution I was stuck with.
Note that all my inbuilt pages will ALWAYS contain the trailing slash, so users and search engines will NEVER see the redirect. The only time the redirect will occur will be when SE's spider the site and remove the trailing slash *cough* Yahoo *cough* and then user's click through links to my site. Thus, I won't get the "redirect penalty" that Google assigns when it encounters an automatic redirect on a website.
The redirect happens so quickly though that you don't even realize it occurred, so it doesn't interfere with user navigation either.
Here is my final set of rewrite rules, without needing a cond to keep images and the css links from breaking:
RewriteRule ^([^/]+)/([^/]+)/([^/]+)/([^/]+)/ $1.php?id=$2&gal=$3&page=$4 [L]
RewriteRule ^([^/]+)/([^/]+)/([^/]+)/ $1.php?id=$2&gal=$3 [L]
RewriteRule ^([^/]+)/([^/]+)/ $1.php?id=$2 [L]
RewriteRule ^([^/]+)/$ $1.php [L]
Effect:
/file/id/gal/page/ turns into file.php?id=4&gal=name&page=2
/file/id/gal/ turns into file.php?id=4&gal=name
/file/id/ turns into file.php?id=4
/file/ turns into file.php
Woohoo :)
[edited by: jdMorgan at 7:46 pm (utc) on July 29, 2004]
[edit reason] Fixed code-formatting problem [/edit]