Apache2 mod_rewrite and %{REQUEST_FILENAME}

Apache2 mod_rewrite and %{REQUEST_FILENAME}

I’m trying to develop a new website to increase my php object oriented skills. For this new website, I want every request for any url that doesn’t match a actual file on the disk to be redirected to index.php (to handle parameters in fact). Easy with apache2 rewrite rules :

         RewriteCond %{REQUEST_FILENAME} !-f
         RewriteCond %{REQUEST_FILENAME} !-d
         RewriteCond %{REQUEST_FILENAME} !-l                                                                                                                                                                              
         RewriteRule ^/(.*)$         /index.php?rt=$1 [L,QSA]

This means : if the requested file is not a real file, and isn’t a directory, and isn’t a symlink, then redirect to index.php.

I was really surprised to discover that it doesn’t work. Though, everybody seems to use this syntax ! I checked my apache version : Apache/2.2.9 (Debian), nothing special with this one I guess.
To understand what Apache was doing with my rewrites, I activated the rewrite log :

         RewriteLog /var/log/apache2/rewrite.log                                                                                                                                                                      
         RewriteLogLevel 5


Here’s what I got (the interesting part, cause I got a looot more !) :

[blah blah blah] (2) init rewrite engine with requested uri /toto.htm
[blah blah blah] (3) applying pattern '^/(.*)$' to uri '/toto.htm'
[blah blah blah] (4) RewriteCond: input='/toto.htm' pattern='!-f' => matched
[blah blah blah] (4) RewriteCond: input='/toto.htm' pattern='!-d' => matched
[blah blah blah] (4) RewriteCond: input='/toto.htm' pattern='!-l' => matched
[blah blah blah] (2) rewrite '/toto.htm' -> '/index.php?rt=toto.htm'

So apaches verifies only ‘/toto.htm’ and not the whole path for “%{REQUEST_FILENAME}”? I thought though it was the whole path… let’s verify in the doc.
From http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html, by habit (cause I used apache 2.0 a lot more than apache 2.2 from now on) :

REQUEST_FILENAME : The full local filesystem path to the file or script matching the request.

Hmm. But I use apache version 2.2, so what do they say here http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html :

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.

Ow.

REQUEST_URI : The resource requested in the HTTP request line. (In the example above, this would be “/index.html”.)

Ok, I understand, I use virtual hosts (like everybody, uh?), so the real syntax for my needs is :

         RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-f
         RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-d
         RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-l                                                                                                                                                                              
         RewriteRule ^/(.*)$         /index.php?rt=$1 [L,QSA]

This works even if it doubles the “/” between each variable (one / at the end of DOCUMENT_ROOT, and another at the beginning of REQUEST_FILENAME).

Here’s the rewrite log showing that it works :

[blah blah blah] (2) init rewrite engine with requested uri /toto.htm                                                                          
[blah blah blah] (3) applying pattern '^/(.*)$' to uri '/toto.htm'
[blah blah blah] (4) RewriteCond: input='/path/to/documentroot//toto.htm' pattern='!-f' => not-matched
[blah blah blah] (1) pass through /toto.htm

Now I can disable this log if I want to keep space on my disk.

I must admit I read the description for REQUEST_FILENAME in apache2.2 several times before noticing that it was just the answer… too used to read too fast! Thanks to this old post that made me re-read slower ! 😉

12 thoughts on “Apache2 mod_rewrite and %{REQUEST_FILENAME}

  1. Haha.
    Rewrite rules are magical, but can quickly become as complicated as hell.

    See you soon in the train… Some day?

    Alex from Maizières 😉

  2. Yeyy!! Finally!! You save my whole day. Thank you.
    I also surprise most of people keep using that not working syntax.

  3. Would this be a universal handler for all versions?

    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-d
    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-l

    since document root might be applied twice on some servers?

  4. hmm, Actually that fails miserably for aliases, and could break depending on AcceptPathInfo. I guess one could double up on the rules and use
    %{DOCUMENT_ROOT}%{REQUEST_FILENAME} or
    %{REQUEST_FILENAME}

  5. Thank you, thank you, thank you! (and my good Google skills, you are far from the 1st hit on Google, so I’ll try to share your link some more)

    Apparently this works correctly for use with htaccess files, but not with vm config, which is the best way to go, if you have full control over your host. – and since loads of people use PHP to begin with because it has cheap hosting, it is hard to navigate to better tips like this one.

    Thank you again! 🙂

  6. Excellent, thank you!

    I too *assumed* “RewriteCond %{REQUEST_FILENAME} !-f” would match until I need to download .csv files.

  7. Most people are using the rules within .htaccess so the directory is known, or within a Directory block would work as someone else pointed out. Putting rules in a VirtualHost block is rare which is why most people’s code that you see didn’t work for you.

Leave a Reply

Your email address will not be published. Required fields are marked *