Etienne Kneuss

home » news » Truth about the Last mod_rewrite's modifier (revisited)

SPL Datastructures

The Standard PHP Library was recently completed by a couple of data structures, namely heaps and doubly linked lists. You can find more information about those on
php.net/spl.datastructures

Feeds available

You can read colder.ch directly from the rss/atom feeds:
rss general RSS (general)
rss php RSS (PHP)
atom general Atom (general)
atom php Atom (PHP)

New design : Clearblue

I've made a new skin using the same design with blue colors: Clearblue. Check it out

New website

As you can see, the website has changed completely. I've re-designed everything using different types of technology to give an overall improvement. Take a look in the news section for detailed information.

Truth about the Last mod_rewrite's modifier (revisited) The 27th of January 2007 @ 22:25

Table of Content

  1. Introduction
  2. Truth about the Last modifier
  3. How mod_rewrite works
  4. Pitfalls
  5. Where L shouldn't be used
  6. Conclusions

Introduction

From what I have seen in many tutorials or in IRC discussions, I must say that the usage of the [L] (or Last) modifier has become some sort of a standard, a flag that everyone should put there "just in case", as it can't be harmful. This often demonstrates bad understanding on how apache's mod_rewrite internally works. I'll try to explain why [L] should be used wisely, and especially in which cases you shouldn't use it at all.

Truth about the Last modifier

Let's focus on rewriteRules in a per-directory context (a .htaccess file for example), which sadly represents the majority of mod_rewrite's uses. In this context, the [L] flag won't force the rewrite to stop, it will only triggers the internal sub-request at this point. But the sub-request may get through a set of rewriteRules defined in a per-directory context again, meaning that the rewrite of the current URL may not be finished.

Let's consider a simple example:

# /path/to/webroot/.htaccess RewriteEngine on rewriteRule a b [L] rewriteRule b c

Most people would think that when requesting /a, the internally requested file would be /b because of the [L] flag. Well, it's not. The last requested file will actually be /c. To understand why, you need to understand how mod_rewrite handles a request in a per-directory context.

How mod_rewrite works

First, there is an important difference between a set of rewriteRules in a per-directory context and one placed in a more general context like in the httpd.conf file. The way mod_rewrite will work is determined by the context in which the rewriteRules are defined. Because mod_rewrite was ment to rewrite URLs to files, it won't be able to directly handle requests in a .htaccess file, where the requests will have already been translated to files. For that reason, mod_rewrite will try to translate them back to URLs and use them in an internal sub request, which will restart the API phases. Read API Phases for more details on that subject.

It means that when an url is rewritten in a per-directory context, it will trigger a sub-request, wich may be rewritten again, and again.

Applying that to the example, we get:

1st sub-request - Trying to apply the pattern "a" to "a": match -> rewrite to "b". 1st sub-request - L flag used: trigger internal sub-request with the rewritten url. 2nd sub-request - Trying to apply the pattern "a" to "b": no match. 2nd sub-request - Trying to apply the pattern "b" to "b": match -> rewrite to "c". 2nd sub-request - Reaching the end, url was rewritten, trigger internal sub-request with the rewritten url.. 3rd sub-request - Trying to apply the pattern "a" to "c": no match. 3rd sub-request - Trying to apply the pattern "b" to "c": no match. 3rd sub-request - Reaching the end, url was not rewritten: serve the file.

Pitfalls

A common pitfall is a rewriteRule that ends in an infinite loop. For example:

# /path/to/webroot/.htaccess RewriteEngine on rewriteRule ^(.*)$ page_$1.php

Let's say we request bar, it will get rewritten to page_bar.php which will get rewritten to page_page_bar.php.php and so on. Adding a [L] flag here won't help at all.
There are multiple solutions here. One would be to write your rewriteRules in a more general context. Another solution would be to ensure that mod_rewrite won't get in an infinite loop. You could either use a rewriteCond or simply modify the pattern so it can't match the rewritten url.

Where [L] shouldn't be used

As a general rule, don't use [L] flag when you're defining only one rewriteRule, it's useless. Additionally, don't use [L] when another rewriteRule in the file is able to match the rewritten url. Doing so will only give more work to the rewrite engine. To demonstrate that, let's consider the first example without any [L] flag:

1st sub-request - Trying to apply the pattern "a" to "a": match -> rewrite to "b". 1st sub-request - Trying to apply the pattern "b" to "b": match -> rewrite to "c". 1st sub-request - Reaching the end, url was rewritten, trigger internal sub-request with the rewritten url. 2nd sub-request - Trying to apply the pattern "a" to "c": no match. 2nd sub-request - Trying to apply the pattern "b" to "c": no match. 2nd sub-request - Reaching the end, url was not rewritten: serve the file.

It would require only two sub-requests for the same result. In that case, the [L] flag is better left out.

Conclusions

I hope this little explanantion will help people in understanding how the [L] (Last) flag and more generally mod_rewrite works in a per-directory context. Additionnaly you should never define a set of rewriteRules in a per-directory context, as it will be slower and you may run into the kind of troubles explained above.

Comments

29.01.2007 #1 colder

The last version contained some mistakes and unclear assumptions, sorry for that. Thanks Joe for pointing that out.

I believe the new version better reflects how it really works.

01.02.2007 #2 joe

ok. this version is better. but there is another way how to stop infinite loops.
do you know the trick with %{ENV:REDIRECT_STATUS} ??

29.10.2008 #3 Bob

Finally a good explanation of the L flag... thanks a lot

25.03.2009 #4 wundbread

Massive thanks never really understood L and was wondering why it didn't work right :)

07.05.2009 #5 Kay Ching

This is the article I was looking for. Thanks!

But I have to disagree with the view of people having "bad understanding on how apache's mod_rewrite internally works"

It is more that mod_rewrite's interface is a putrid piece of ill-thought out shit. It may be powerful but the design is totally scatterbrained dung dug out from the depths of satan's toilet bowl.

07.05.2009 #6 Kay Ching

And another thing I realized, since you "shouldn't use [L] when another rewriteRule in the file is able to match the rewritten url", [L] is actually effectively useless except for some presumably niche, specialized situations.

24.11.2009 #7 Java Guru

I am trying to use the L-flag for my web application.

I want my request to http://mydomain.com/hello to point to index.php?handle=$1 (much like myspace).

So far it is working, but my session is being detroyed and logging out the user? If I use the R-flag, it redirects ... and the application works fine.

Any ideas on why this might be happening?

Thanks.

23.12.2009 #8 Geert

Good article. While browsing through the Apache manual, I also learned about the [NS] flag which will skip rewrite rules if the current request is an internal sub-request. (I'd rename "1st sub-request" to "1st request" in your article, just to be clear.)

Add a comment

Username:

Spam Challenge: 2+16=?

Comment: