Search code examples
apache.htaccessmod-rewriteurl-rewriting

Why does mod_rewrite process the rules after the [L] flag


There are the rules:

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.+) dir/index.php?$1 [L]
RewriteRule dir/index\.php.* - [F]

Why the last rule is processed and it returns Forbidden for all requests?

I need that if file or directory is not found then the next rule shouldn't be processed.

The next example isn't working for me as well:

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule .? - [S=1]
RewriteRule dir/index\.php.* - [F]
RewriteRule (.+) dir/index.php?$1

It still returns Forbidden for all requests.


Solution

  • Why the last rule is processed and it returns Forbidden for all requests?

    When the URL foobar is requested:

    • The two conditions (line 2, 3) match
    2   RewriteCond %{REQUEST_FILENAME} !-f
    3   RewriteCond %{REQUEST_FILENAME} !-d
    
    • Pattern matches, the resulting URL becomes dir/index.php?foobar with (line 4)
    4   RewriteRule (.+) dir/index.php?$1 [L]
    

    Note that the [L] flag causes the rewriting to stop; but it does not stop Apache from having another go at the rewritten URL since it has changed (see below about handling .htaccess directives).

    With dir/index.php as the input URL:

    • The condition does not match (line 2) since file exists
    • Jumps to line 5
    5   RewriteRule dir/index\.php.* - [F]
    
    • Pattern matches, hence the Forbidden error

    When directory or filename changes, Apache has to re-evaluate various configuration sections (e.g. Directory and Files) and the .htaccess file for the "re-written" path. This is why Apache might perform another iteration even when the previous one was ended with the [L] flag.

    The last string supposes to restrict the direct access to UFL handler. Direct access means requesting the file through a link like: domain.com/dir/index.php

    I think adding another condition before line 5 should work:

    RewriteCond %{THE_REQUEST} dir/index\.php\x20HTTP/\d\.\d$
    RewriteRule . - [F]
    

    The THE_REQUEST server variable contains the request sent by the browser without any rewriting applied. This could be useful to detect what page was originally requested by the browser.

    THE_REQUEST

    The full HTTP request line sent by the browser to the server (e.g., "GET /index.html HTTP/1.1"). This does not include any additional headers sent by the browser. This value has not been unescaped (decoded), unlike most other variables below.