9

I have a problem with a file test operation in a mod_rewrite RewriteCond entry which is testing whether %{REQUEST_FILENAME} exists. It seems that rather than %{REQUEST_FILENAME} being an absolute path, I'm getting a path which is rooted at the DocumentRoot instead.

Configuration

I have this inside a <VirtualHost> block in my apache 2.2.9 configuration:

RewriteEngine on
RewriteLog /tmp/rewrite.log
RewriteLogLevel 5

#push virtually everything through our dispatcher script
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^/([^/]*)/?([^/]*) /dispatch.php?_c=$1&_m=$2 [qsa,L]

Diagnostics attempted

That rule is a common enough idiom for routing requests for non-existent files or directories through a script. Trouble is, it's firing even if a file does exist.

If I remove the rule, I can request normal files just fine. But with the rule in place, these requests get directed to dispatch.php

Rewrite log trace

Here's what I see in the rewrite.log

init rewrite engine with requested uri /test.txt
applying pattern '^/([^/]*)/?([^/]*)' to uri '/test.txt'
RewriteCond: input='/test.txt' pattern='!-f' => matched
RewriteCond: input='/test.txt' pattern='!-d' => matched
rewrite '/test.txt' -> '/dispatch.php?_c=test.txt&_m='
split uri=/dispatch.php?_c=test.txt&_m= -> uri=/dispatch.php, args=_c=test.txt&_m=
local path result: /dispatch.php
prefixed with document_root to /path/to/my/public_html/dispatch.php
go-ahead with /path/to/my/public_html/dispatch.php [OK]

So, it looks to me like the REQUEST_FILENAME is being presented as a path from the document root, rather than the file system root, which is presumably why the file test operator fails.

Any pointers for resolving this gratefully received...

Paul Dixon
  • 1,516
  • 3
  • 23
  • 37

4 Answers4

4

Took me a while to find out as well, but the reason is mentioned in the mod_rewrite documentation:

REQUEST_FILENAME The full local filesystem path to the file or script matching the request, if this has already been determined by the server at the time REQUEST_FILENAME is referenced. Otherwise, such as when used in virtual host context, the same value as REQUEST_URI.

That's why it works in .htaccess but not in the conf file of the vhost. (And that's also why adding the DOCUMENT_ROOT works around the problem).

MrWhite
  • 12,647
  • 4
  • 29
  • 41
zpea
  • 143
  • 5
3

I resolved this by explicitly writing the document root into the condition

RewriteCond /path/to/my/public_html%{REQUEST_FILENAME} !-f
RewriteCond /path/to/my/public_html%{REQUEST_FILENAME} !-d
Paul Dixon
  • 1,516
  • 3
  • 23
  • 37
  • 4
    This is strange considering the documentation says REQUEST_FILENAME contains "the full local filesystem path". Did you try with %{SCRIPT_FILENAME}?. Anyways... consider using %{DOCUMENT_ROOT} rather than a literal path for a more general solution. – GetFree Oct 25 '09 at 19:25
  • I know, hence my confusion. I think it might because I've done this before via .htaccess, but this time put the rules into the vhost config - perhaps it operates differently at that level. Thanks for the DOCUMENT_ROOT tip though. – Paul Dixon Oct 26 '09 at 08:27
  • 1
    it's an old question, but being the selected answer, can you edit it so as to use %{DOCUMENT_ROOT}? – tokland Feb 17 '11 at 12:15
  • `RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-f` works perfectly (apache 2.4.XX). – cherouvim Sep 24 '21 at 07:58
2
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

This won't work as intended in a virtualhost (or server) context because the directives are processed before the request has been mapped to the filesystem, unlike when used in a directory (or .htaccess) context. Before the request has been mapped to the filesystem, REQUEST_FILENAME is the same as REQUEST_URI, ie. the root-relative URL-path.

However, you can use a lookahead instead to get the absolute filename that the request will eventually map to. For example:

RewriteCond %{LA-U:REQUEST_FILENAME} !-f
RewriteCond %{LA-U:REQUEST_FILENAME} !-d

OR, construct the absolute filename from the DOCUMENT_ROOT and REQUEST_URI (root-relative URL-path). For example:

RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-d
MrWhite
  • 12,647
  • 4
  • 29
  • 41
0
RewriteCond     %{REQUEST_URI}     ^(/pathinfo/|/pathinfo)(.*)$
RewriteRule     .*          %{DOCUMENT_ROOT}%{REQUEST_URI} [L]