Forum Moderators: phranque
http://example.com/folder/a/detail.php
http://example.com/folder/a/b/detail.php
http://example.com/folder/a/b/c/detail.php
http://example.com/folder/a/b/c/list.php
http://example.com/folder/ is a real folder that i want to have my .htaccess file in.
basically i'd like to have everything after folder and before the scriptname written to a variable
http://example.com/folder/detail.php?var=a
http://example.com/folder/detail.php?var=a/b
http://example.com/folder/detail.php?var=a/b/c
http://example.com/folder/list.php?var=a/b/c
it would be nice if i could grab the name of "folder" dynamically too since this will be implemented in multiple directories.
It looks like the pattern would be ^(([^/]+/)*[^/]+)/[^./]+\.php$ and you would make a back-reference to $1 to populate the value for "var=".
If you're willing to accept a trailing slash on the var= value, then that pattern simplifies to ^([^/]+/)+[^./]+\.php$
If the code goes into /folder/.htaccess, then "folder/" will not appear in the URL-path examined by the RewriteRule, and so does not need to be matched explicitly. It will, however, need to appear in the substitution (target) URL-path.
See the references cited in our Forum Charter for more information.
Jim
[edited by: jdMorgan at 11:47 pm (utc) on April 7, 2009]
RewriteRule ^(([^/]+/)*[^/]+)/([^./]+\.php)$ /$2$3?var=$1 [QSA]
however $2 returns the second folder deep and not the first... unless i wrote the right side of the argument wrong.
the request
http://example.com/folder/test/105/detail.php
tries to resolve to
/test/detail.php and not /folder/detail.php
Thanks for your help so far!
Something like:
RewriteRule ^(([^/]+/)*[^/]+)/([^./]+\.php)$ /folder/$3?var=folder/$1 [QSA,L]
Jim
So then if I wanted it to be more dynamic I could put the rewrite rules in the root .htaccess file? I tried this and several other iterations with no success.
RewriteRule ^([^/]+)/(([^/]+/)*[^/]+)/([^./]+\.php)$ /$1/$4?var=$2 [QSA]
The output of this RewriteRule won't match the rule's own pattern, so it doesn't look like it will cause an infinite loop, which is the most common problem. So how did you test (what were the URLs you used), what did you expect, what were the results, and how did those results differ from your expectations?
RewriteRule ^([^/]+)/(([^/]+/)*[^/]+)/([^./]+\.php)$ /$1/$4?var=$2 [QSA,L]
Jim
i tried
http://example.com/folder/a/b/detail.php
and every time it told me that
/folder/a/b/detail.php could not be found. and from what i gather if it tries to resolve to the original request, that means it couldn't match anything to the pattern.
I expected that adding the first part in () would make $1 the value of the first folder and push all the other variables up a number. But since it never tried to resolve to anything other than the original request, I couldn't tell.
I did have the L in there to start off with but it was giving me a server error so I took it out and the server error went away... kinda miffed me.
Either both of the following lines or only the second line --it varies depending on server set-up-- is required ahead of your rule:
Options +FollowSymLinks
RewriteEngine on
BTW, to avoid confusing test results if you're trying to add these lines, use a super-simple rule such as
RewriteRule ^foo\.html$ http://www.webmasterworld.com/ [R=301,L]
I think I may have just found the problem. I tried this rewrite
RewriteRule ^(.*)$ /folder/detail.php [QSA,L]
while folder is a real directory and my request was
http://example.com/folder/a/b/detail.php
however if changed my request to
http://example.com/folder1/a/b/detail.php
it resolved to
http://example.com/folder/detail.php
is this because rewrite rules outside of real folders are ignored? Like my example... it would only obey rewrite rules found in /folder/.htaccess if i was making a request to http://example.com/folder/anything
if thats true, is there anyway to accomplish what i want at all?
/folder/.htaccess file can only deal with requests for URLs that resolve to that folder. You'll need the
.htaccess file to be in the root if it needs to deal with other URLs. You need to think both about URLs as they appear 'on the web' and the folder/file structure on the server. They are merely 'related' and not the same thing.
.
This rewrite creates an infinite loop:
RewriteRule ^(.*)$ /folder/detail.php [QSA,L] The output matches the input pattern and it rewrites again.
/.htaccess
RewriteRule ^folder1/(([^/]+/)*[^/]+)/([^./]+\.(.*))$ /_folder1/$3?rewrite_id=$1 [QSA,L]
RewriteRule ^folder2/(([^/]+/)*[^/]+)/([^./]+\.(.*))$ /_folder2/$3?rewrite_id=$1 [QSA,L]
/_folder1 and /_folder2 are the real folder names. this rule solves exactly what i originally wanted, but its created a new problem. requests to something like /folder1/file.php fail since its matching multiple folders deep only. any ideas to tweak the rule to account for this?
or would it be possible to progressively rewrite the URL?... ex: request /folder1/a/b/detail.php
step 1: rewrite /folder1/a/b/detail.php to /_folder1/a/b/detail.php (but don't submit yet)
step 2: check if /_folder1/a/b/detail.php actually exists. if it does, then submit the rewrite.
step 3: if its not a real file rewrite to /_folder1/detail.php?a=a/b
The problem is that as coded you have a / after folder1, then another / after the bracketed part. This means the bracketed part has to exist to match the pattern.
You need to get the second / moved inside the brackets and then apply something (? or *) to make that part of the pattern optional.
Write out a complete list of different URL formats that could be requested, mark which ones have to be reused as backreferences, look for patterns, and think it through again.
my current ruleset
/.htaccess
RewriteRule ^folder1/(([^/]+/)*[^/]+/)?([^./]+\.(.*))$ /_folder1/$3?rewrite_id=$1 [QSA,L]
RewriteRule ^folder2/(([^/]+/)*[^/]+/)?([^./]+\.(.*))$ /_folder2/$3?rewrite_id=$1 [QSA,L]
so if i have the real directories
/_folder1/images/
requests to http://example.com/folder1/images/img.jpg will be rewritten to http://example.com/_folder1/img.jpg?rewrite_id=images/
i always want to rewrite the folder name, but then only rewrite the rest of the request IF it doesn't resolve to a real file. So I guess i will need to do a progressive style rewrite, but i'm not sure how to accomplish this.
(i do realize it could be done by rewriting the directory name from within the root .htaccess file and then within each folder have an .htaccess file that checks weather its a real file and further processes it. However I will have many folders like this and it seems like bad form to duplicate the same ruleset so many times. It also seems like it would create extra requests that are not neccessary.)
RewriteRule ^(folder1¦folder2)/(([^/]+/)*)([^./]+\..+)$ /_$1/$4?rewrite_id=$2 [QSA,L]
--
The problem with "real" directories needs to be described in detail, with examples. It's not at all clear.
If you mean that folder1 and folder2 have physically-existing subdirectories and that those subdirectories should not be affectd by this rule, then you could use:
RewriteCond %{DOCUMENT_ROOT}/$1/$2 !-d
RewriteRule ^(folder1¦folder2)/(([^/]+/)*)([^./]+\..+)$ /_$1/$4?rewrite_id=$2 [QSA,L]
For this reason, you should consider either moving the 'real' directories and files out of /folder1 and /folder2, or using different 'virtual directories' -separate from folder1 and folder2- to create your 'virtual' URLs. This would prevent having to make the 'directory-exists' disk check for every request.
If you mix 'real' and 'virtual' URL-paths, then you also have the problem that you can never create a virtual subdirectory that has the same name as a real subdirectory, and vice-versa. This leads to errors and to maintenance headaches.
In other words, use a directory structure such as
/folder1-real/<all physically-existing files and folders>
/folder1-virtual/<all paths here and below are rewritten to the script>
-or-
/folder1/real/<all physically-existing files and folders>
/folder1/virtual/<all paths here and below are rewritten to the script>
In this way, the URL carries all the information needed by a RewriteRule to determine whether it should execute.
Jim
I definitely see the issues involved with virtual/real directories. i can avoid most issues with overlapping names ahead of time and i can't even think of a scenario when this would be a problem. I just have that weird feeling that it could come up in the future so i'd like to have my ruleset account for it ahead of time. so for that reason and pure curiosity i'm curious how to accomplish this. here is my best effort so far.
RewriteRule ^folder1/(.*)$ /_folder1/$1
RewriteRule ^folder2/(.*)$ /_folder2/$1
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteRule ^/([^/]+/)/(([^/]+/)*[^/]+/)?([^./]+\..*)$ /$1/$4?rewrite_id=$2 [QSA,L]
i know whats in the first set of () in the second rewriterule is not correct... i want to grab the name of the first folder. ex. _folder1. and also i don't think %{SCRIPT_FILENAME} is what i'm looking for in the rewritecond. i want to test against the currently written url at the point that it matches the second rewriterule
RewriteRule ^folder1/(.*)$ /_folder1/$1
RewriteRule ^folder2/(.*)$ /_folder2/$1
RewriteCond /$1/$4 -f
RewriteRule ^/([^/]+)/(([^/]+/)*[^/]+/)?([^./]+\..*)$ /$1/$4?rewrite_id=$2 [QSA,L]
What i want to accomplish is only apply the second rule if what would result is a real file. If i comment out the Cond, it rewrites properly. But as is it doesn't think the Cond is true.
If you're having trouble getting that path right, then temporarily change the rule to a redirect instead of a rewrite, and copy that %{DOCUMENT_ROOT}/$1/$4 tpath expression into a 'fake' query string variable appended to the RewriteRule substitution. The redirect will result in the new URL being sent to your browser, where you can inspect the composite filepath in the query string.
Jim
when i plug in an absolute directory for the rewrite conditions it works properly. so can you think of a work around for how to grab the VirtualDocumentRoot?
If the v_host to document-root "map" is small and simple, then you could reproduce it in mod_rewrite by checking requested %{HTTP_HOST} to get the hostname, and base which filepath you check on that. The simplest case would be one rule-set for each domain, with the 'file-exists' path customized for each. Code economy might then be improved after working out the basic function.
If the v_host to document-root map isn't simple and small, then it sounds like it's time to re-architect your server configuration, URLs, and file structure from the ground up based on your current needs; there are some 'tangles' you can get into that are impossible or just too troublesome to fix in any other way.
Jim
jim:
the "map" is simple. it is:
[dir.example.com...] = /dir/example.com
it would be super delicious if i could actually have a function in my server httpd.conf or vhosts.conf that modifies the value of DOCUMENT_ROOT without having to create an entry for each domain. it seems like that defeats the purpose of having a VirtualDocumentRoot.
i guess second best option would be to have a rule-set in the root .htaccess file that modifies the DOCUMENT_ROOT value. but if this is possible, wouldn't it be possible to have it in the httpd.conf and work the same?
if neither of those are options, i would have to use backreferences to the parts of the HTTP_HOST to get generate the full document root anytime i wanted to call it?
RewriteCond %{HTTP_HOST} ^([^.]+)\.([^.]+\.[^.:]+)\.?(:[0-9]+)?$
RewriteCond /www/public/html/%1/%2/$1/$4 -f
RewriteRule ^/([^/]+)/(([^/]+/)*[^/]+/)?([^./]+\..*)$ /$1/$4?rewrite_id=$2 [QSA,L]
RewriteCond %{HTTP_HOST} ^([^.]+)\.([^.]+\.[^.:]+)\.?(:[0-9]+)?$
RewriteRule ^.? - [E=myDocRoot:/www/public/html/%1/%2]
RewriteCond %{myDocRoot}/$1/$4 -f
Jim, that works like a charm. I thought i read somewhere that you can put rewrites in the httpd.conf. I tried using the code below but it doesn't have the same result as putting it in the .htaccess. I'm testing this in a windows environment if that makes a difference.
<IfModule rewrite_module>
Options +FollowSymLinks
RewriteEngine on
RewriteCond %{HTTP_HOST} ^([^.]+)\.([^.]+\.[^.:]+)\.?(:[0-9]+)?$
RewriteRule ^.? - [E=myDocRoot:%{DOCUMENT_ROOT}/%1/%2]
<IfModule>
If putting that code in httpd.conf didn't have the same result as in .htaccess, then what result *did* it have?
The only thing that differs with respect to this code is that you may not need to declare the Options in this block of code if it's in httpd.conf; You can do if you like, but it will still affect only .htaccess files, and not the present code. (See Options directive in Apache Core documentation.)
You can also dump the <ifModule> container. It's only needed if you want the code to fail silently when mod_rewrite is not installed on a server.
Jim