Main Configuration

At the heart of each webserver is the configuration file. It controls the whole behaviour and therefore has to be mighty but at the same time easy enough to get the desired results without hassle.
In the lighttpd config you control how it reacts on requests. To achieve this you have to express some kind of logic – just like in a programming language.

Basic Syntax

The syntax for the lighttpd 2.0 configuration file is somewhat similar to various programming languages – kind of a mixture. But don’t be afraid, it is really simple. No pointers involved :)
The basic blocks are values, variables, function calls and conditions.



There are two boolean values:

  • true
  • false


Integers are stored as signed ints with 64 bits (max value around 9 * 10^18). There are three basic ways to use them:

  • decimal integers: starting with any non zero digit, like 128
  • octal integers: starting with a zero, like 0644
  • hexadecimal integers: starting with 0x like 0xff

All three can have a suffix representing a factor the number gets multiplied with:

  • byte: 1
  • kbyte: 1024
  • mbyte: 1024*1024
  • gbyte: 1024*1024*1024
  • tbyte: 1024*1024*1024*1024
  • pbyte: 1024*1024*1024*1024*1024
  • bit: 1 / 8
  • kbit: 1024 / 8
  • mbit: 1024*1024 / 8
  • gbit: 1024*1024*1024 / 8
  • tbit: 1024*1024*1024*1024 / 8
  • pbit: 1024*1024*1024*1024*1024 / 8
  • sec: 1
  • min: 60
  • hours: 3600
  • days: 24*3600

For sizes the base unit is byte, and for intervals seconds.


There are 4 ways to specify a string:

  • 'hello world'
  • "hello world"
  • e'hello world'
  • e"hello world"

The basic escape rules are the same for both:

  • "\n" is a newline, "\r" a carriage return, "\t" a tab stop
  • "\\" is one \, "\"" is one double quote " and "\'" a single quote '
  • escaping single/double quote is optional if the symbol is not used to terminate the string, i.e. '\"' = '"' and "\'" = "'"
  • "\xNN": NN must be hexadecimal characters, and the string is replaced with the decoded 8-bit value as a single byte
  • All other \ occurences are not removed from the string.

The e'..' and e"..." variant do not allow any occurences of the last kind to happen.

All other characters are allowed (the strings are parsed as 8-bit binary; lighttpd2 usually doesn’t care about the encoding).


Lists are ordered collections of other values:

  • [1, true, "foo"] (simple list)
  • [] (empty list)
  • [1] (list with one element)
  • [[1,2],[3,4],5] (nested lists)
  • [1,2,] (final value can have a trailing , too)
  • (1, 2, 3) (alternative brackets with more than one element)

Note that (1) is not a list; parentheses can also be used to group expressions as in 1*(2+3), and it is therefore recommend to use only [] to specify lists.

Key-Value Lists

Key-value lists associates keys to values (both can be of any type); the syntax is:

  • [ "a" => 1, "b" => 10 ]

As with normal lists the final value can have a trailing , too. You cannot mix simple values and key-value associations in one list (nesting them works).

The key => value operator is also only syntactic sugar for a [key, value] list, the above example is the same as:

  • [ [a, 1], [b, 10] ]

This especially means that key-value lists are ordered too, although they can get converted to hash tables in certain function calls internally.


There are operators available for some value types:

  • for integers: +, -, * and /
  • for strings: + (concatenate two strings)
  • for lists: + (append lists)

Also you can cast strings and booleans to integers and any value to strings:

  • cast(int) "256" (only supports decimal representations and no suffix like above)
  • cast(int) true (true maps to 1 and false to 0)
  • cast(string) 5

Expressions can be grouped to override default association:

  • 3 * (1 + 2) vs. 3 * 1 + 2 and so on

Action Blocks

An action block consists of a list of function calls (in action context) and conditionals; it is treated like a value, i.e. can be assigned to variables and used as parameter in function calls.

Action blocks can also contain variable assignments and setup blocks/function calls, which are not part of the “action block value” itself, but are evaluated while parsing the config.

The syntax is:

	log "hello world";
	if req.path =$ ".hidden" { static; }

The complete config is an action block too (but without the surrounding curly braces); conditionals also use action blocks for their branches.

Each action block that is not a condition branch starts a new nested variable scope;


Variable names start with a alphabetic character (a-z and A-Z) or an underscore _, and are followed by alphanumeric characters, underscores _ and dots .; keywords are not allowed as variable names.

Variables store values, and the name can be used in place of an actual value; later modifications to a variable have no influence on previous uses (i.e. are only evaluated while parsing the config).

Variables are assigned values with = (and a terminating ;)

my_types = [".txt" => "text/html"];
php = {
	if phys.path =$ ".php" { fastcgi "unix:/var/run/lighttpd/php.sock"; }

By default variables assignment overwrites an existing variable (in its previous scope) or, if it doesn’t exist, creates a new one in the local scope (i.e. it will only be available in the current scope and nested descendants).

You can explicitly create a new variable in the local scope (hiding variables in parent scopes with the same name) by prefixing the assignment with local:

local wwwpath = "/var/www/";

You can also create variables in the global scope by prefixing the assignment with global.
The main config already is in a nested scope (i.e. not the global scope). The global scope is not destroyed after config loading, and can be used in delayed config loading (say from SQL in the future).

If a variable name is used in a context it will always use the definition from the nearest scope.


This example illustrates that variables are evaluated while parsing the config.

foo = "bar";
if req.path == "/somepath" {
	foo = "baz";

# at this point foo will ALWAYS contain "baz" no matter if "/somepath" was requested or not


This example illustrates scoping.

foo = "bar";
php = {
	local foo = "baz";
	# in this block (and nested blocks within) foo is now "baz"
	# ...
# foo is now "bar" again

Special Variables

sys.* variables are readonly. right now the following sys.* variables are available:

  • the process id of lighttpd
  • sys.cwd: the current working directory
  • sys.env.X: the system environment variable with name X (for any X)

Function calls

There are three types of function calls:

  • actions
  • setups
  • options

Actions can only be used in action (block) context, setups only in setup (block) context, and options can be used in both.

A setup context is started with the keyword setup, and is followed by either a single setup function call or a setup block.

Setup function calls are run immediately when they occur (they are used to “setup” the webserver environment, like listening on TCP sockets, and setting default options), while action function calls are run for each request (mapping request urls to physical paths, handling requests, modifying the default options).

The actions, setups and options are provided by the modules.


Includes are similar to function calls in the syntax, but are directly handled by the config parser. They are only allowed in action context, as they insert a reference to an action block at the point they are used.

There are three types of includes:

  • include "/etc/lighttpd/vhosts/*.conf";: include files like the main config itself; the path can contain wildcards
  • include_shell "/etc/lighttpd/";: runs the specified command, and parses the output of it as config file
  • include_lua "/etc/lighttpd/complex.lua": includes a Lua config file. The single action to be executed must be returned in the global actions variable (or leave it empty to do nothing). See also lua.handler

Includes also create a new nested scope.

Debug Print

Similar to includes __print is a special function, but is available in action and setup context. It logs the string values of its parameter(s) with log level “debug”.


Conditions (known from Lighttpd 1.x) are the equivalent to "if"s in most programming languages. There are also “else” and “elseif” equivalents.
They are created by a comparison of a condition variable and a value that is evaluated at runtime (for each request).
Conditions can be nested, and you can group them with and and or operators. and binds stronger than or, although it is preferred to group them with parentheses.


if == "mydomain.tld" {
	if req.path == "/" or req.path == "/index.html" {
	} else if req.path == "/upload" {
		if req.content_length > 100mbyte {
		} else {
			proxy "";


The basic syntax forms are:

  • if <expr> { ... }
  • if <expr> { ... } else { ... }
  • if <expr> { ... } else if <expr2> { ... }
  • if <expr> { ... } else if <expr2> { ... } ... (continue with else or else if)

A condition expression <expr> is:

  • (<expr>)
  • <expr1> and <expr2>
  • <expr1> or <expr1> (<expr1> and <expr2> or <expr3> = (<expr1> and <expr2>) or <expr3>; and has higher precedence)
  • <condvar> <op> <value> for <condvar> being a condition variable (with a type different from boolean), <op> an condition operator and <value> a string or a number.
  • <condvar> or !<condvar> for a boolean condition variable.

Condition variables

There are three categories of condvars:

  • (can be abbreviated by
  • (can be abbreviated by
  • (can be abbreviated by
variable description
request.localip ip address of the listing socket, the client connected to (filename for unix sockets)
request.localport port number of the listening socket, -1 for unix sockets
request.remoteip ip address of the client
request.remoteport port number of the client, -1 for unix sockets
request.path the path part of the requested url. not including the querystring.
request.raw_path the raw path (not urldecoded, not simplified) of the requested url, including the querystring. requested hostname
request.scheme scheme of the request. “http” or “https”
request.query the querystring of the requested url
request.method method of the request. “GET”, “POST”, “HEAD” etc.
request.length integer. length of the content for e.g. POST methods
request.header[“name”] request header name e.g. request.header[“referer”]
request.is_handled boolean condition, does request already have a handler (static, fastcgi..)
request.environment[“name”] (or short request.env[“name”]) CGI environment
physical.path physical path of the file to be served. e.g. document root + path
physical.exists boolean condition, indicates whether the requested file (normal file, directory or even a special file) exists
physical.size integer. size of the requested file. -1 if file doesn’t exist
physical.is_dir boolean condition, indicates whether the requested file is a directory
physical.is_file boolean condition, indicates whether the requested file is a normal file (e.g. no unix socket etc)
physical.docroot document root
physical.pathinfo pathinfo
response.status response status code (blocks request until response header is available)
response.header[“name”] response header (blocks request until response header is available)

Condition operators

op description op description
== compares two values on equality != negative ==
<= less than or equal < less than
>= greater than or equal > greater than
=~ regular expression match !~ negative =~
=^ prefix match !^ negative =^
=$ suffix match !$ negative =$
=/ cidr match !/ negative =/