electronic brain surgery since 2001

Lighty Catches

I recently switched all my sites from Apache and suPHP to lighttpd and fastcgi. The latter solution is much faster and responsive, but lighttpd is not quite as easy to configure. What follows here is not a rant. I'm quite happy with lighty and below is just a list of things I found to be tricky during my quest.

I did install the lighttpd Debian package. It installs an interesting system to enable and disable server modules by setting symlinks to config snippets. Interesting but overkill in my opinion - just ignore it. Another thing not quite right in the Debian config is the order of modules. In lighttpd the module order matters - you should adjust the server.modules list to this 1):

server.modules   = ( "mod_rewrite",
                     "mod_accesslog" )

When you set up multiple virtual hosts you might come across the following oddity. Many examples on the net do configure a separate errorlog directive for each virtual host. But errorlog seems to ignore any conditionals and each occurence overwrites the preceding one. So keep in mind: server.errorlog is global - set it only once! A guy in the lighttpd IRC channel told me he is already working on a patch to change this behaviour.

The documention on certain lighty features is scarce on details sometimes. I followed the excellent “Setup FastCGI and PHP with individual user permissions” but wasn't sure which influence the PHP_FCGI_CHILDREN variable in the startup script had. I finally found out with a simple test.

I wrote a simple PHP script which blocks for 15 seconds, then set my PHP_FCGI_CHILDREN to 5 and used Apache Benchmark to fetch it 5 times with a concurrency of 5. As expected ab finished in 15 seconds. Repeating the experiment with 10 concurrent fetchs doubled the time approving what I already suspected. The number of spawned PHP processes limits how many PHP requests can be handled at the same time. While lighttpd has no such bottlenecks it self and simply scales up through threading it's clever event driven approach2), you still have a limit here. The question is how to figure the ideal pool size of PHP processes for a website?

Note that there is no way to disabling fastcgi for a certain directory. The trick is not to register the fastcgi handler for that directory in the first place by using a conditional. Strangely enough the following did not work:

# doesn't work
$HTTP["url"] != "^/somedir" {
    fastcgi.server = ( ".php" => ( fast cgi options here ) )

It seems lighty is a bit picky about what conditionals it uses for initializing fastcgi and only considers positive checks. So you need to move the negation into the regexp:

# works just fine
$HTTP["url"] =~ "^/(?!somedir)" {
    fastcgi.server = ( ".php" => ( fast cgi options here ) )

Another thing that caused me a few gray hairs is how rewrites and redirects are handled in lighttpd. While in Apache both are handled through mod_rewrite, there are two separate modules in lighty. Though I told you earlier that the load order of modules matters, it doesn't for the rewrite and redirect ones. The rewrite rules are always processed before the redirects. This is a real problem if you have a very greedy rewrite to send nearly everything to a single dispatcher 3). Have a look at these rules which are an subset of the ones I used here at www.splitbrain.org:

url.redirect = (
    "(?i)^/Fortunes/"    => "/projects/fortunes",
    "(?i)^/Programming/" => "/projects",

url.rewrite-once = (
    "^/(.*)/?$" => "/doku.php?id=$1",

The redirects forward users from locations at my old page to the new places, but with lighty executing the rewrites first they never match. I asked at the mailinglist about that and there seems to be no real solution except adding all redirects as rewrites as well. The trick is to rewrite them to themself and use rewrite-once (similar to Apache's [L] modifier) to stop rewriting on a match:

url.redirect = (
    "(?i)^/Fortunes/"    => "/projects/fortunes",
    "(?i)^/Programming/" => "/projects",

url.rewrite-once = (
    "(?i)^/Fortunes/"    => "$0",
    "(?i)^/Programming/" => "$0",
    "^/(.*)/?$" => "/doku.php?id=$1",

You see with some trickery lighty is able to replace even more complex Apache setups. A complete list of rewrites to use DokuWiki's userewrite option with light is available in the Wiki.

lighty, lighttpd, linux
Similar posts:
of course only add the modules you really need
I just got a great explanation for dummies by weigon at IRC - a really detailed description is available, too
like in DokuWiki