It can be a little bit tricky (or at least I've not found a clear description on this topic in the Apache docs) to tell where the mod_rewrite module of Apache 2.2.x is going to look for a file if you use a condition. I've had a hard time to get this clear and maybe sharing my experience will benefit others.
It's a common practise (eg. Drupal uses it) to place the following piece of code into a .htaccess file:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
It works like this: if both conditions (
RewriteCond
lines) are fulfilled, then the rewrite rule is triggered. The two conditions check that the requested URI does not exist neither as a file, nor as a directory. However how does it really work?
If you place the
RewriteCond
directive in a .htaccess file, then the
REQUEST_FILENAME
variable will contain the concatenation of the full path of the directory (where the .htaccess is located) and the requested URI.
So if the .htaccess is in a directory like "/var/www/example/dir", the DocumentRoot is "/var/www/example" and the requested URI is "http://www.example.com/dir/somedir/somefile", then
REQUEST_FILENAME
will take the value of "/var/www/example/dir/somedir/somefile".
But what happens if you place the same rule into the server config or a virtual host config? The answer is: it depends.
To me it appeared fairly logical that the base dir for
REQUEST_FILENAME
will be the DocumentRoot of the given virtualhost, but it seems mod_rewrite developers thought otherwise (and I must admit they're right, but imho having this in the official documentation would spare some hours of scratching heads for most people). If you place the
RewriteCond
directive in the root section of the virtualhost config, then nothing will be prepended to the requested URI. Using the previous example (the DocumentRoot is "/var/www/example" and the requested URI is "http://www.example.com/dir/somedir/somefile") the value of
REQUEST_FILENAME
will be "/somedir/somefile"!
However if you place the
RewriteCond
inside a
<Directory>
directive, it'll suddenly start working as if it was in a .htaccess file (the path of the given directory will be prepended to the requested URI)! Thus if you want to move a mod_rewrite rule from a .htaccess file to a virtual host config and it relies on conditions on the
REQUEST_FILENAME
variable, then you should place the rewrite rule in a
<Directory>
container so you won't have to prepend the base directory in the "
RewriteCond %{REQUEST_FILENAME} !-f
" expression.
If for some reason you don't want to do this, you can still include the
DocumentRoot
in the rewrite condition like this:
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-f
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php?q=$1 [L,QSA]
Note the small difference in the
RewriteRule
too: you've to use absolute pathes now, which in our case means adding a slash (
/
) before the index.php URI.
P.S.: using the
RewriteBase
directive in a .htaccess file complicates things a little bit further, but it's fairly easy to follow and understand so I'm not going into details on that.
P.S.2: after having gone through hours of debugging, I've found a
Drupal specific page that deals with this mod_rewrite configuration madness and has the same solution that I came up with. You might want to read it since it contains a lot of insightful comments from fellow visitors. I've added some clarification in a
comment regarding the issue with broken pages (missing style sheets, etc.) in case no $base_url is provided in settings.php.
Comments
Thank you, sir!
The problem was precisely what you describe here, so thanks for the tip.