Configuration syntax


lighttpd configuration syntax is basic and many configurations can be expressed simply in the configuration syntax.

However, the configuration syntax is not a full programming language, nor does it pretend to be. For any complex logic, it is recommended to create a script which produces lighttpd configuration syntax as output.

BNF like notation of the basic syntax

  option   : NAME = VALUE
  merge    : NAME += VALUE
  replace  : NAME := VALUE    (replace/overwrite earlier value) (since 1.4.46)
  NAME     : modulename.key
  VALUE    : ( <string> | <integer> | <boolean> | <array> | VALUE [ + VALUE ]*)
  INCLUDE  : "include" <string>
  INCLUDE_SHELL : "include_shell" <string>

  <string> : '"' [text] '"'           (can be constructed from multiple stringifiable values: VALUE [ + VALUE ]*)
  <integer>: digit+
  <boolean>: ( "enable" | "disable" | 1 | 0 )
  <array>  : "(" [ [ <string> "=>" ] <value>, ]* ")" 
    (key => value) list : "(" [ <string> "=>" <value>, ]* ")" 
    value list          : "(" [               <value>, ]* ")" 

   LOCAL_VARIABLE      : "var." NAME
                       : "var." NAME ( = | += | := ) VALUE
   ENVIRONMENT_VARIABLE: "env." NAME    (lighttpd.conf environment at startup)


  # default document-root
  server.document-root = "/var/www/" 

  # TCP port
  server.port = 80

  # selecting modules
  server.modules = ( "mod_access", "mod_rewrite" )

  # variables, computed when config is read.
  var.mymodule = "foo" 
  server.modules += ( "mod_" + var.mymodule )
  # var.PID is initialised to the pid of lighttpd before config is parsed

  # include, relative to dirname of main config file
  include "mime.types.conf" 

  # read configuration from output of a command
  include_shell "/usr/local/bin/ /etc/mime.types" 

Conditional Configuration

Most (non-global) options can be configured conditionally by using the following syntax (including nesting).

  <field> <operator> <value> {  # (since 1.4.74, may be preceded by literal "if")
    <field> <operator> <value> {
      ... nesting: match only when parent match
  else <field> <operator> <value> { # (since 1.4.74, "elif", "elsif", "elseif", or "else if" are also accepted in lieu of "else")
    ... the "elseif" block
  else { # (since 1.4.46)
    ... the "else" block

where <field> is one of one of the following:

Field name Description
$REQUEST_HEADER["..."] match on arbitrary HTTP request header (case-insensitive) (since 1.4.46)
$HTTP["request-method"] match on the request method (since 1.4.19)
$HTTP["scheme"] match on the scheme used by the incoming connection. This is either "http" or "https" (since 1.4.19)
$HTTP["host"] match on host
$HTTP["url"] match on url path (not including host or query-string)
$HTTP["querystring"] match on querystring, e.g. after the ? in this type url: index.php?module=images...
$HTTP["remoteip"] match on the remote IP address or a remote network == or != CIDR mask (works with IPv6 since 1.4.40)
$HTTP["cookie"] (subsumed by $REQUEST_HEADER["Cookie"] since 1.4.46) match on Cookie
$HTTP["useragent"] (subsumed by $REQUEST_HEADER["User-Agent"] since 1.4.46) match on User-Agent
$HTTP["language"] (subsumed by $REQUEST_HEADER["Accept-Language"] since 1.4.46) (since 1.4.21) match on Accept-Language
$HTTP["referer"] (subsumed by $REQUEST_HEADER["Referer"] since 1.4.46) match on Referer
$SERVER["socket"] match on socket (local address, not remote address). Value must be on the format "ip:port" -- where ip is an IP address(optional) and port is a port number -- or must be unix domain path. If IP address is omitted, then use INADDR_ANY (, unless server.use-ipv6 = "enable" inside this block, in which case use in6addr_any ([::]). Setting this directive with == also instructs lighttpd to bind to this socket and listen for requests. $SERVER["socket"] is valid in global scope; placing $SERVER["socket"] inside other conditions may have undesirable results (and would be rejected if not for historic (mis)use).


<operator> is one of:

Operator Value
== string equal match
!= string not equal match
=~ perl style regular expression match
!~ perl style regular expression not match
=^ string prefix match (since 1.4.65)
=$ string suffix match (since 1.4.65)

and <value> is a quoted ("") string, either a string literal or regular expression.


  # disable directory-listings for /download/*
  dir-listing.activate = "enable" 
  $HTTP["url"] =~ "^/download/" {
    dir-listing.activate = "disable" 

  # handle virtual hosting
  # map all domains of a top-level-domain to a single document-root
  $HTTP["host"] =~ "(^|\.)example\.org$" {
    server.document-root = "/var/www/htdocs/" 

  # multiple sockets
  $SERVER["socket"] == "" {
    server.document-root = "..." 

  $SERVER["socket"] == "" {
    ssl.pemfile = "/var/www/certs/localhost.pem" 
    ssl.engine = "enable" 

    server.document-root = "/var/www/htdocs/" 

  # deny access for all googlebot
  $HTTP["useragent"] =~ "Google" {
    url.access-deny = ( "" )

  # deny access for all image stealers (anti-hotlinking for images)
  $HTTP["referer"] !~ "^($|http://www\.example\.org)" {
    url.access-deny = ( ".jpg", ".jpeg", ".png" )

  # deny the access to to all user which 
  # are not in the network
  $HTTP["host"] == "" {
    $HTTP["remoteip"] != "" {
     url.access-deny = ( "" )

  # Allow only and to
  # have access to
  $HTTP["host"] == "" {
    # !~ is a perl style regular expression not match
    $HTTP["remoteip"] !~ "^(200\.19\.1\.5|210\.45\.2\.7)$" {
      $HTTP["url"] =~ "^/admin/" {
        url.access-deny = ( "" )

Conditional Configuration Merging

lighttpd configuration is parsed and optimized at startup. The configuration is static after startup.

At runtime, dynamic configuration selection is limited evaluating the request using the lighttpd condition syntax and the static configuration, e.g. matching against static lists parsed at startup.

Put another way, various configurations objects are static after startup, including backend server configuration. When parsing requests at runtime, a backend server may be selected, but the configuration of the backend is static after startup.

Options in different sections of the configuration are not merged dynamically at runtime. For a given option, the value of that option in the last matching condition is the value that is applied. Within the same lighttpd condition { ... } and same nesting level of { ... }, you can use +=, which is parsed at startup, but += does not apply across different lighttpd conditions { ... } or different nesting levels { ... } within a condition.

At the same scope level (same level of { } nesting), identical condition blocks are merged during parsing at startup. Note that syntax errors may result from repeating a condition in if-else statements if the condition is already present at the same scope level (e.g. in other if-else statements). Again, the configuration syntax is not a full programming language, nor does it pretend to be. For any complex logic, it is recommended to create a script which produces lighttpd configuration syntax as output.


If you're not running on the default port, $HTTP["host"] will have the port appended to it, so regular expressions ending in $ (without allowing for a port) won't match.
To match with or without a port, change


Note that some earlier versions of lighttpd do not support the full configuration file syntax listed here. In particular, some versions do not support "var." variables, appending with "+=", nested conditionals, or "else" blocks. The names of some options (for example, "server.dir-listing") have also changed (i.e. to "dir-listing.activate") between versions of lighttpd.

If you're having trouble configuring lighttpd, consider using the "-t" or "-p" options to debug your configuration. Note that some earlier versions of lighttpd not support the "-t" or "-p" options.

Advanced usage

Check the blog:

Using variables

You can set your own variables in the configuration to simplify your config.

Note: Variables are expanded at startup when the configuration is parsed; variables are not expanded at runtime for each request.

  var.basedir = "/home/www/servers/" 
  $HTTP["host"] == "" { = "" 
     include "incl-base.conf" 

In incl-base.conf:

  server.document-root = basedir + + "/pages/" 
  accesslog.filename   = basedir + + "/logs/access.log" 

You can also use environment variables or the default variables var.PID and var.CWD.

  var.basedir = env.LIGHTTPDBASE

  $HTTP["host"] == "" { = "" 
     include "incl-base.conf" 
     include "incl-fastcgi.conf" 

In incl-fastcgi.conf:

  fastcgi.server = ( ... => ((
     "socket" => basedir + + "/tmp/fastcgi-" + PID + ".sock" 
  )) )

Or like the lighttpd script for rails does:

  var.basedir = var.CWD
  server.document-root = basedir + "/public/" 


(non-exhaustive list)

The configuration syntax is not a full programming language, nor does it pretend to be. For any complex logic, it is recommended to create a script which produces lighttpd configuration syntax as output.

Some useful things that can NOT be done in lighttpd config: (Instead, you may create a script in a real programming language and then use include_shell.)

  # testing if a variable has been set is NOT possible
  var.not_sure_if_it_exists == undefined { ... set to default value ... }

  # removing from arrays is NOT possible
  server.modules -= ( "mod_idontwantyou" )

Global context

  global {

You don't need it in the main configuration file.
But you might have difficulty setting a server wide configuration inside a included-file from conditionals.


In lighttpd.conf:

  server.modules = ()
  $HTTP["host"] == "" {
    include "incl-php.conf" 

In incl-php.conf:

  global {
    server.modules += ("mod_fastcgi")
    static-file.exclude-extensions += (".php")
  fastcgi.server = "..." 


All Configuration Options can be found at: Configuration Options

Updated by gstrauss 8 months ago · 61 revisions