Project

General

Profile

Actions

MigratingFromApache » History » Revision 46

« Previous | Revision 46/51 (diff) | Next »
gstrauss, 2021-05-21 21:18


Migrating from Apache to lighty

Configuration Basics

Lighttpd is a great option as a replacement for existing apache or apache2 systems. If you are seriously considering migrating, read the documentation for the configuration file Docs:Configuration. The configuration syntax initially has the most significant learning curve, but you will see its flexibility once you familiarize yourself with it. One of the most powerful features of the lighttpd configuration is its conditionals. Lighttpd allows you to setup configuration options based on the clients' request. Conditionals can be nested, so you can create some very complex, yet effective configuration options. Conditionals are the key to many solutions which people encounter when migrating from apache/apache2. Here are some examples:

Problem: I want to disable directory listing for directory /download:
Entry in Configuration file:

####### Enable dir listing for all directories
dir-listing.activate = "enable" 

####### If the URL is like ^/download/ then disable dir-listing
$HTTP["url"] =~ "^/download/" {
  dir-listing.activate = "disable" 
}

You should familiarize yourself with regular expressions to truly become familiar with conditionals. You will use conditionals in your configuration file(s) for a multitude of things. Authentication, URL Rewriting, Virtual Hosting, and many more. All the variables you can use in conditionals are listed here along with further explanation about the match operators Docs:Configuration.

'''
Problem''': You want the status page to be available just for users from the local network:
Entry in configuration file:

$HTTP["remoteip"] == "10.0.0.0/8" {
  status.status-url = "/server-status" 
}

And here is a nested example.

Problem: If the host is www2.example.org and the directory is /download enable directory listing:

$HTTP["host"] == "www2.example.org" {
  server.document-root = "/var/www/servers/www2.example.org/pages/" 
  $HTTP["url"] =~ "^/download/" {
    dir-listing.activate = "enable" 
  }
}
For more conditional examples read:

Basic Options

Options +FollowSymLinks becomes server.follow-symlink = "enable"

Accesslog

Accesslogs are written by mod_accesslog and support the same options in accesslog.format = ... as Apache.

If you need logfile rotation use one of two ways:

logrotate

If you don't use the debian package copy ./debian/lighttpd.logrotate to /etc/logrotate.d/

logrotate will send lighttpd a SIGHUP when it is time to rotate the logs and lighttpd will reopen the logs accordingly.

cronolog

With cronolog you pipe the accesslog to a pipe and let a external program handle the logfile writing:

accesslog.filename = "|/usr/sbin/cronolog /web/logs/%Y/%m/%d/access.log" 

mod_rewrite

mod_rewrite is more difficult as the implementation of how the rules are written is completely different.

First of all, we always match on the full relative request-uri that is submitted by the user. That means we can emulate apache's QSA flag (query string append; see mod_rewrite in Apache) by matching on the query string and adding it back to the target:

url.rewrite = ( "^/something/(\d+)(?:\?(.*))?" => "/index.php?bla=$1&$2" )

The important thing is (?:\?(.*))? which matches on the query string and is inserted in the target url using $2 (the query string after ? is the second matching bracket pair; ?: makes the brackets non-capturing, i.e. the content is not available via $x as we don't need it).

The following example is based on a problem from http://dir.onlinesearch.ws/ sent in by dbird@freenode. link is inactive as of January 24, 2007.

RewriteEngine On
RewriteBase /instadir/
RewriteCond %{REQUEST_FILENAME}  -d
# Fix trailing slash problem
RewriteRule ^(.+[^/])$           $1/  [R,L]
# Do not try to treat the following resources as parameters to index.php
RewriteRule ^index\.php.*$       - [L]
RewriteRule ^dmoz\.css$          - [L]
RewriteRule ^admin[/]?.*$       - [L]
RewriteRule ^img[/]?.*$         - [L]
RewriteRule ^[/]{0,}(.*)$ index.php?area=browse&cat=$1 [QSA,L] 

These rewrites want to rewrite everything that is not the index.php, dmoz.css, admin-interface or something from the image-directory to a parameter of the index.php page. The base directory for this match is ''/instadir/''.

## for all URLs in /instadir/ that are not index.php, dmoz.css, admin or img, do ...
url.rewrite-once = ( "^/instadir/(?!index\.php|dmoz\.css|admin|img).*" => "$0", 
                     "^/instadir/([^?]*)(?:\?(.*))?" => "/instadir/index.php?area=browse&cat=$1&$2")

The first pattern is using regex-magic called zero-width negative look-ahead assertion and is something from the advanced chapters of your regex book.

In lighttpd 1.4.50 and later, the above can be simplified to

## for all URLs in /instadir/ that are not index.php, dmoz.css, admin or img, do ...
url.rewrite-once = ( "^/instadir/(?!index\.php|dmoz\.css|admin|img).*" => "",
                     "^/instadir/([^?]*)" => "/instadir/index.php?area=browse&cat=$1${qsa}")

mod_fastcgi

If you have 2 extensions assigned to the fastcgi handler in Apache like

AddType fastcgi-php .php .phtml

then you need two entries in the fastcgi.server config:

fastcgi.server = ( ".php" => (( "bin-path" => "/my/fastcgi-php", 
                                "socket" => "/path/to/php.socket" )),
                   ".phtml" => (( "socket" => "/path/to/php.socket-0" ))
                 )

The first entry will create the php-fcgi process and the second will reuse the same socket.

If you need to use PHP and Python in the same setup, just add two extensions:

fastcgi.server = ( ".php" => (( "bin-path" => "/my/fastcgi-php", 
                                "socket" => "/path/to/php.socket" )),
                   ".py" => (( "host" => "127.0.0.1", "port" => 3200 ))
                 )

For this setup python was spawned externally and is a waiting for requests at port 3200, localhost.

!MultiViews

How to setup a similar mechanism to the Apache's MultiViews option ?

Fortunately, Christian Hoffman wrote a lua script to do that. Here is how to use it :

server.modules = ( "mod_magnet", )
server.document-root = "/var/www" 
magnet.attract-physical-path-to = ( server.document-root + "/multiviews.lua" )

Another option is content-negotiation.lua

Directory !ForceType Migration

There are several PHP packages and tutorials that use the apache ForceType directive instead of mod_rewrite. They use it as a Search Engine friendly way to pass arguments as fake directories.

Let say you have this setup:
  • Your script is called news (note no php extension)
  • Your URLs look like /news/100/ (/news/_id of the news_)
  • Your script grabs $_GET!['PHP_SELF'] to determine the news id

and apache is configured with a Location or LocationMatch entry like this:

<Location /news>
   ForceType application/x-httpd-php
</Location>
The easiest way to migrate this is:
  • First setup PHP with lighttpd and make sure .php extension is working
  • Rename filename news to news.php
  • Use a lighttpd rewrite to replace the Forcetype entry

In lighttpd configuration file:

url.rewrite-once = ("^/news/.*" => "/news.php")

If your script is still not working, try to replace
$_GET!["PHP_SELF"] with $_GET!["REQUEST_URI"] You can find it usually at the top of the script.
PHP does not register PHP_SELF correctly when running in FastCGI.

Pretty URLs (or '.php' script auto-detection)

I had a problem after migrating in duplicating my old Apache1.x + PHP setup had '''http://server.com/script/blah''' happily running ''script.php_ with arguments _/blah''. This is really useful for easily making pretty URLs.

I couldn't see an easy solution to replicate this behaviour, or any entries in the docs here, so I decided to share my findings. Essentially there are two ways to go about replicating this in a semi-generic way so that you don't have to recode / rename your actual scripts.

If you were happy to rename you could probably make things work using lighttpd's forcetype support.

mod_rewrite solution

This is the one I picked because it's faster and seemed neater. Basically I added

include "mod_rewrite.conf" 

... and then created that file with the contents ...

url.rewrite-repeat = ( "/script/(.*)" => "/script.php/$1" )

I guess you can also do this in a semi-generic way by naming each script:

url.rewrite-repeat = ( "/(script|script2|script3)/(.*)" => "/$1.php/$2" )

404 handler solution

This is probably minutely slower but may be a better idea if you have more complex (ie: not pure regex) considerations in your URL rewriting. You can also replicate the regex above using PHP preg_match(). Just add:

server.error-handler-404 = "/404.php" 

... then write your handler in that file.

!ScriptAlias

$HTTP["host"] == "example.com" {
            alias.url  = ( "/bin/" => "/path/to/my/bin/" )
            $HTTP["url"] =~ "^/bin" {
                    cgi.assign = ( ".pl" => "/usr/bin/perl" )
            }
}

Another solution could be to use mod_alias with:

cgi.assign = ( ".py" => "/usr/bin/python" )
alias.url = (
    "/chrome" => var.basedir + "/foo.org/chrome",
    "" => var.basedir + "/foo.org/dispatch.py" 
)

The lack of an ending '/' will work as a wildcard.

Inside this script, get the environment variable 'REQUEST_URI' to figure out the 'pretty' ending string.

In python this would be: os.environ['REQUEST_URI'] (omitting import), in php: $_SERVER['REQUEST_URI'].

Updated by gstrauss over 3 years ago · 46 revisions