Forum Moderators: phranque
I've been trying to use some variation of the following three code segments, but when the trailing slash rule is in place it always cancels the php extension rule regardless of where it is in the code or how it's been implemented. Without the trailing slash rule, URLs are automatically redirected (due to mod_dir I assume, which I have no control over on my server) from http://example.com/directory to http://example.com/example.com/directory/. I've tried dozens of different variations of these rules, and I'm out of ideas on how to get them working together.
## Add trailing slash on directories
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^/*(.+/)?([^.]*[^/])$ http://%{HTTP_HOST}/$1$2/ [L,R=301]
## Remove the need for .php extension
RewriteCond %{REQUEST_FILENAME}!-d
RewriteCond %{REQUEST_FILENAME}!-f
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteCond %{REQUEST_URI}!/$
RewriteRule ^(.*)$ $1.php [L]
# Aim all domains from /htdocs to /htdocs/example.com/
RewriteCond %{REQUEST_URI}!example1.com/
RewriteCond %{REQUEST_URI}!example2.com/
RewriteCond %{REQUEST_URI}!example3.com/
RewriteRule ^(.*)$ /%{HTTP_HOST}/$1 [L]
## Add missing trailing slash to directory URLs
RewriteCond %{REQUEST_URI} !/$
RewriteCond %{REQUEST_FILENAME}/ -d
RewriteRule (.*) http://%{HTTP_HOST}/$1/ [R=301,L]# Map all domains into /htdocs/<domain>/ subdirectories
# (user variable subdirectoryRewriteDone prevents a loop & eliminates
# the need to check for each possible domain subdirectory)
RewriteCond %{ENV:SDrDone} !^Yes$ [NC]
RewriteRule (.*) %{HTTP_HOST}/$1 [E=SDrDone:Yes]
## Map extensionless URL requests to .php files
RewriteCond %{REQUEST_URI} !/$
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule (.*) $1.php [L]
I also changed the extensionless-URL-to-php-file rewrite. It now gives priority to existing php files, instead of to existing extensionless files. That's because a 'real' extensionless file isn't likely to be useful when served via HTTP (It can't be assigned a valid MIME-type, for example). And checking for a trailing slash eliminates the need to check extensionless files as existing directories -- If they existed, then the first rule would have already added a slash to them and redirected. Therefore, there's no need to check for 'directory exists' for a second time.
The purpose of these changes is to eliminate, remove, or add conditions to as many 'file-exists' and 'directory-exists' checks as possible. 'Exists' calls to the operating system's filesystem manager are relatively slow --especially if the actual disk has to be accessed, and should be avoided or optimized whenever possible. 'Exists' checks should always be done last in a 'stack' of RewriteConds to avoid doing them if any preceding condition fails.
I changed the rule order as well. The order shown assumes that each domain has its own php scripts, located in that domain's subdirectory -- If that's the case, I believe you'll want to do both rewrites sequentially before ending mod_rewrite processing.
If that is not the case, then reverse the order of the last two rules, and put the [L] flag back on the domain-to-subdirectory rewrite rule.
I also suggest you look into canonicalizing your domains, so that you don't have www.example.com and example.com mapped to two different subdirectories...
Jim
One other problem - the trailing slash rule still exposes the sub-directory that the domain is mapped to (http://example.com/example.com/directory/).
Strangely, if I comment out
RewriteCond %{REQUEST_FILENAME}/ -d then the sub-directory is properly hidden. Why would this be? Also, canonicalization of my domains is next on my list. Thanks for the reminder.
[added]
Try omitting the trailing slash on the RewriteCond test-string:
RewriteCond %{REQUEST_FILENAME} -d You do need to test whether the directory exists, so that's a likely fix.
[/added]
On the internal server error, please post the relevant error info from your server error log. I use an almost-identical piece of code on several of my servers to map subdomains to subdirectories, so it would be helpful to see what error you're getting.
Another approach is to put all your domains' files into second-level subdirectories, such as "/sites/<domain>/<files>. Then the RewriteCond can simply check for "sites/" in the URL-path to prevent a rewrite loop. However, the lockout flag method is better because it allows for any domain or subdomain name to be used without a false match, *and* allows for first-level domain-subdirectories.
Anyway, post the error log entry for that 500 error if possible.
Jim
[edited by: jdMorgan at 5:58 am (utc) on June 21, 2007]
I've discovered that as soon as a RewriteCond starts checking for directories then all of my redirects stop working as expected. The redirects still occur, but once it has finished going through all of the rules it automatically shoehorns in the subdirectory at the beginning of the URL.
Luckily I've found a partial solution:
RewriteCond %{REQUEST_URI}!/$
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule [b]^example.com/?[/b](.*) http://%{HTTP_HOST}/$1/ [R=301,L]
Please provide an example request URL and the resulting "shoehorned" rewritten URL-path, so we can be crystal-clear on this point.
For the HTTP_HOST problem, another way to code that would be:
RewriteCond %{REQUEST_URI} !/$
RewriteCond %{REQUEST_FILENAME} -d
RewriteCond %{HTTP_HOST} (.+)
RewriteRule ^example.com/?(.*) http://%1/$1/ [R=301,L]
Jim
Code:
RewriteCond %{REQUEST_URI}!/$
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule (.*) http://%{HTTP_HOST}/$1/xyz [R=301,L] Code:
RewriteCond %{REQUEST_URI}!/$
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^example.com/?(.*) http://%{HTTP_HOST}/$1/xyz [R=301,L] For the HTTP_HOST problem, another way to code that would be:
...RewriteRule ^example.com/?(.*) http://%1/$1/ [R=301,L]
Oops, my post was completely ambiguous. I was referring to the pattern rather than the substitution. This is what I first tried:
RewriteRule ^[b]%{HTTP_HOST}[/b]/?(.*) http://%{HTTP_HOST}/$1/ [R=301,L] This only seemed to work when I created an explicit rule for each of my domains, like this:
RewriteRule ^[b]example.com[/b]/?(.*) http://%{HTTP_HOST}/$1/ [R=301,L] I'm probably missing something simple (variables can't be used in the pattern?)
Unfortunately, I still can't spot any clue as to what might be wrong with this setup, and need to ponder it more when I get more time. Have a look at the RewriteBase directive, and see if that might be applicable to your situation. Also, it may be possible to move some of this code into .htaccess files in subdirectories, if that might simplify it.
Jim
RewriteEngine On
RewriteBase /
## Add missing trailing slash to directory URLs
RewriteCond %{REQUEST_URI}!/$
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule (.*) http://%{HTTP_HOST}/$1/ [R=301,L]
Does this seem appropriate, barring a more elegant solution?