RewriteRule Last [L] flag not working?

php_flag display_errors 1
php_value auto_prepend_file init.php
RewriteEngine on 
RewriteRule ^$  /id/authenticate [R]
RewriteRule ^login_openid$  /id/login_openid.php [QSA,L]
RewriteRule ^authenticate$  /id/authenticate.php [QSA,L]
RewriteRule ^facebook$  /id/facebook.php [QSA,L]
RewriteRule ^createfromopenid$  /id/createfromopenid.php [QSA,L]

RewriteRule .* - [L,R=403]

This is my .htaccess file. In the serverconfig I just have AllowOVerride all.

If I request the URL http://mydomain.com/id/authenticate I get a 403 Error. If I remove the last rule, it works. Shouldnt the [L] flat prevent any further rules from happening?

Edit:

My htaccess file is in the subfolder "id", so the rules work.


The [L] rule works fine -- you just do not know how it actually works.

When Apache sees the [L] flag and rule matches (rewrite occurs), Apache will go to next iteration and will start matching all rules again from top. The [L] flag means "do not process any rules below in this iteration".

Yes, the Apache documentation is not 100% clear on this (which means it can be improved), but provides enough info to figure it out eventually.


Apache will stop rewrite cycle in few situations:

  1. No rules matched at all (no rewrite occurred);

  2. "exit now" rule matched (e.g. RewriteRule .* - [L]);

  3. Rewrite occurs, but input URL and final URLs are the same (happens on 2nd-3rd iteration when "badly" written rule rewrites the same URL to the same URL.

    For example RewriteRule (.*) /index.php?page=$1 [L]:

    • /hello => /index.php?page=hello
    • on next iteration it will rewrite /index.php => /index.php?page=index.php
    • and on 3rd iteration it will be /index.php => /index.php?page=index.php .. which makes no sense now);
  4. Rewrite iteration limit is reached (by default = 10) -- that's if you entered infinite rewrite cycle (the value is controlled by LimitInternalRecursion Directive).


With all aforementioned information I can say that your current rules do work as expected. This means that you have to change the logic and get rid of the last rule (maybe handle this moment in parent .htaccess .. or handle it differently -- all depends on how your application is built, I do not want to make wild guesses).


Put this in front of your cath all rule.

RewriteCond %{ENV:REDIRECT_STATUS} !=200

The problem is that once the [L] flag is processed, all the next RewriteRules are indeed ignored, however, the file gets processed AGAIN from the begin, now with the new url.

This magic Condition will not process the catch all if the file was already redirected.

PS: If it doesn't work, you might need to tweak the condition a bit: 200, !=200, ^., ^$.
Apparentely the variable gets set to 200 for a redirect, but also other pages (error and stuff) set it to some value. Now that means that you either check if it is empty, is not empty, is 200 or is not 200, depending on what you need.