Lighttpd

From Indie IT Wiki

Web Server Error Response Codes

All HTTP response status codes are separated into five classes or categories. The first digit of the status code defines the class of response, while the last two digits do not have any classifying or categorisation role. There are five classes defined by the standard:

  • 1xx informational response – the request was received, continuing process
  • 2xx successful – the request was successfully received, understood and accepted
  • 3xx redirection – further action needs to be taken in order to complete the request
  • 4xx client error – the request contains bad syntax or cannot be fulfilled
  • 5xx server error – the server failed to fulfill an apparently valid request

https://en.wikipedia.org/wiki/List_of_HTTP_status_codes

Performance Tweaks

Compression

Create directory for virtual hosts' cache...

mkdir /var/cache/lighttpd/www.domain.com
chown -R www-data:www-data /var/cache/lighttpd/www.domain.com

Edit config file...

compress.cache-dir = "/var/cache/lighttpd/www.domain.com/" 

Turn on zlib compression for PHP...

nano /etc/php/7.2/cgi/php.ini
zlib.output_compression = On

Restart web server...

service lighttpd restart

Test...

curl -I -H 'Accept-Encoding: gzip,deflate' https://www.domain.com/

Expiry Cache Control

 $HTTP["url"] =~ "^/" {
        expire.url = ( "" => "access 5000 days" )
 }
 setenv.add-response-header += (
       "Cache-Control" => "public, must-revalidate, proxy-revalidate"
 )

https://www.cyberciti.biz/faq/lighttpd-send-cache-control-maxage-headers-of-staticfiles/

Etags and Last-Modifed

Before... missing...

$ curl -I https://www.normawinstone.com/wp-content/cache/omgf-webfonts/anton-400-normal-z-C8.woff2
HTTP/1.1 200 OK
Cache-Control: public, must-revalidate, proxy-revalidate
Content-Type: application/octet-stream
Accept-Ranges: bytes
Content-Length: 14220
Date: Sat, 20 Jun 2020 09:29:38 GMT
Server: lighttpd/1.4.45

If the Content-Type is NULL, then set the Content-Type to "application/octet-stream" and disable caching.

In this case disabling caching means that both ETag and Last-Modified are neither calculated, nor sent with the response.

But how to fix that? There were multiple approaches as lighttpd processes /etc/mime.types on Debian. One solution that works for each and every file is defining a fall-back value inside the configuration file.

Why? Because the Content-Type will not be NULL when it reaches the check.

Long story short, the fix was adding these three lines to the bottom of our lighttpd.conf:

mimetype.assign += (
  "" => "application/octet-stream"
)

After... included...

$ curl -I https://www.normawinstone.com/wp-content/cache/omgf-webfonts/anton-400-normal-z-C8.woff2
HTTP/1.1 200 OK
Cache-Control: public, must-revalidate, proxy-revalidate
Content-Type: application/octet-stream
Accept-Ranges: bytes
ETag: "2396403747"
Last-Modified: Fri, 19 Jun 2020 09:42:29 GMT
Content-Length: 14220
Date: Sat, 20 Jun 2020 09:37:29 GMT
Server: lighttpd/1.4.45

https://anexia.com/blog/en/the-tale-of-lighttpd-not-sending-the-last-modified-header/

Improve SSL Security

ssl.engine = "enable"
ssl.pemfile = "/etc/lighttpd/domain_com.pem"
ssl.ca-file = "/etc/lighttpd/domain_com.ca-bundle"
# 
# tweaks - start
#
ssl.dh-file = "/etc/ssl/certs/dhparam.pem"
ssl.ec-curve = "secp384r1"
ssl.use-compression = "disable"
ssl.use-sslv2 = "disable"
ssl.use-sslv3 = "disable"
ssl.cipher-list = "EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4"
#
# tweaks - finish
#

https://raymii.org/s/tutorials/Strong_SSL_Security_On_lighttpd.html

Shell Script To Clean Lighttpd Web Server Cache

https://bash.cyberciti.biz/file-management/cleaning-webserver-cache-script/

HOWTO: Fix Error: All Files Content-Type: application/octet-stream

Instead of displaying web pages, your browser will try to download files.

This is because Lighttpd's mime assign settings are broken.

To fix it, recreate the mime configuration file and change the settings then restart the server...

sudo -i
/usr/share/lighttpd/create-mime.conf.pl > /usr/share/lighttpd/mime.conf
sudo nano /etc/lighttpd/lighttpd.conf
include "/usr/share/lighttpd/mime.conf" 
systemctl restart lighttpd.service
exit

https://redmine.lighttpd.net/boards/2/topics/8820

HOWTO: Fix Error: failed to execute shell

Error...

failed to execute shell: bash -c /usr/share/lighttpd/create-mime.assign.pl: No such file or directory
failed to execute shell: bash -c /usr/share/lighttpd/include-conf-enabled.pl: No such file or directory

Reason...

Because the Ubuntu Release Upgrader has removed PHP during the upgrade process. God knows why.

Solution...

Reinstall the PHP CGI module and the PHP MySQL module.

Fix...

sudo apt-get install php-cgi php-mysql
sudo lighty-enable-mod fastcgi-php
sudo service lighttpd restart

HOWTO: Compile Source Code Latest Version

sudo -i
apt-get install build-essential libpcre3 libpcre3-dev zlib1g-dev libbz2-dev libssl-dev libxml2 libxml2-dev libxml libxml++2.6-dev libsqlite3-dev
mkdir lighttpd
cd lighttpd
wget https://download.lighttpd.net/lighttpd/releases-1.4.x/lighttpd-1.4.55.tar.gz
tar -xzvf lighttpd-1.4.55.tar.gz
cd lighttpd-1.4.55/
./configure --with-openssl
make
make install
wget https://www.xarg.org/download/rc.lighttpd.debian -O /etc/init.d/lighttpd
chmod +x /etc/init.d/lighttpd
/etc/init.d/lighttpd start

HOWTO: Index Page Column Sorting

Example - http://doc.lighttpd.net/

View Source and you will see the CSS and JavaScript.

Documentation - https://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModDirlisting

You will need Lighttpd version 1.4.42+

HOWTO: Lighttpd + PHP

Install the packages we need: (this may not be all, but these two will automatically download the rest as dependencies)

sudo aptitude install lighttpd php-cgi

Enable the fastcgi module and the php configuration with

sudo lighty-enable-mod fastcgi
sudo lighty-enable-mod fastcgi-php

Reload the lighttpd daemon

sudo service lighttpd force-reload

To test if it's working create the file /var/www/index.php with the following contents:

<?php phpinfo(); ?>

Thanks - https://wiki.ubuntu.com/Lighttpd+PHP

HOWTO: Move Default Document Root

sudo mkdir /var/www/default
sudo mv /var/www/index.lighttpd.html /var/www/default/
sudo chown -R www-data:www-data /var/www/default/
sudo nano /etc/lighttpd/lighttpd.conf
server.document-root = "/var/www/default"
sudo /etc/init.d/lighttpd restart

HOWTO: SSL Certificate with Let's Encrypt

http://wiki.indie-it.com/wiki/Let%27s_Encrypt#LetsEncrypt_with_Lighttpd

HOWTO: SSL Secure Certificate Purchase

https://www.ssls.com/

HOWTO: SSL Secure Certificate Generation

https://www.digicert.com/easy-csr/openssl.htm

openssl req -new -newkey rsa:2048 -nodes -out domain_co_uk.csr -keyout domain_co_uk.key -subj "/C=GB/ST=County/L=Town/O=Your Name/OU=Web Site/CN=domain.co.uk"

HOWTO: SSL Secure Certificate Installation

https://www.digicert.com/ssl-certificate-installation-lighttpd.htm

http://billpatrianakos.me/blog/2014/04/04/installing-comodo-positive-ssl-certs-on-apache-and-openssl/

cat COMODORSADomainValidationSecureServerCA.crt COMODORSAAddTrustCA.crt AddTrustExternalCARoot.crt > BundleCA.crt
cat your_domain_name.key your_domain_name.crt > your_domain_name.pem
$SERVER["socket"] == ":443" {
ssl.engine = "enable"
ssl.pemfile = "/path/to/your_domain_name.pem"
ssl.ca-file = "/path/to/BundleCA.crt"
#ssl.use-compression = "disable"
ssl.use-sslv2 = "disable"
ssl.use-sslv3 = "disable"
#ssl.honor-cipher-order = "enable"
#ssl.cipher-list = "AES256+EECDH:AES256+EDH:!aNULL:!eNULL"
ssl.cipher-list = "ECDHE-RSA-AES256-SHA384:AES256-SHA256:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM"
}

HOWTO: Restrict Access To IP Address

$HTTP["remoteip"] !~ "123.456.789.10|66.102.[0-15].[0-255]" {
  url.access-deny = ( "" )
}

or

$HTTP["remoteip"] !~ "66.249.*.*|66.102.*.*" {

}

Thanks - http://serverfault.com/questions/137969/allowing-multiple-ip-ranges-access-to-a-virtual-host-in-lighttpd-with-remoteip#146848

HOWTO: Restrict Access By User Agent Browser String

$HTTP["useragent"] !~ "GoogleDocs" {
  url.access-deny = ( "" )
}

HOWTO: Restrict Access By Request Method

$HTTP["request-method"] !~ "^GET$" {
  url.access-deny = ( "" )
}

or

$HTTP["request-method"] !~ "^(GET|HEAD)$" {
  url.access-deny = ( "" )
}

HOWTO: Password Protect Directory

Create the password file...

http://www.toolsvoid.com/htpasswd-password-generator

Or...

sudo apt-get install apache2-utils
sudo htpasswd -c /etc/lighttpd/.htpasswd username

Add the authentication module to the main configuration file...

        "mod_auth",

Add the following lines to the separate virtual host file...

auth.backend = "htpasswd"
auth.backend.htpasswd.userfile = "/etc/lighttpd/.htpasswd"
auth.require = ( "/webmail/" =>
  (
    "method" => "basic",
    "realm" => "Webmail Access",
    "require" => "valid-user",
  )
)

If you want to ignore localhost and your network each time, use this instead...

$HTTP["remoteip"] !~ "(127.0.0.1|192.168.0.*)" {
  auth.backend = "htpasswd"
  auth.backend.htpasswd.userfile = "/etc/lighttpd/.htpasswd"
  auth.require = ( "/webmail/" =>
    (
      "method" => "basic",
      "realm" => "Webmail Access",
      "require" => "valid-user",
    )
  )
}

HOWTO: Allow Directory Listing

Add the following line to your main configuration file or separate virtual host file...

server.dir-listing = "enable"

...or...

$HTTP["url"] =~ "^/files($|/)" { server.dir-listing = "enable" }

Official Web Page - http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModDirlisting

Thanks - http://www.cyberciti.biz/tips/howto-lighttpd-enable-disable-directory-listing.html

HOWTO: Change Directory Listing Design

Create 2 files called HEADER.txt and README.txt in your web site folder.

These contain the HTML and CSS for your directory listing page (when no index.html is found).

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>Index of /linux/</title>
<link rel="shortcut icon" href="/favicon.ico" />
<style type="text/css">
a, a:active {text-decoration: none; color: blue;}
a:visited {color: #48468F;}
a:hover, a:focus {text-decoration: underline; color: red;}
body {background-color: #F5F5F5;}
h2 {margin-bottom: 12px;}
table {margin-left: 12px;}
th, td { font: 90% monospace; text-align: left;}
th { font-weight: bold; padding-right: 14px; padding-bottom: 3px;}
td {padding-right: 14px;}
td.s, th.s {text-align: right;}
div.list { background-color: white; border-top: 1px solid #646464; border-bottom: 1px solid #646464; padding-top: 10px; padding-bottom: 14px;}
div.foot { font: 90% monospace; color: #787878; padding-top: 4px;}
</style>
</head>
<body>

...and...

</body>
</html>

Then, add these settings to your web site configuration file...

dir-listing.auto-layout = "disable"
dir-listing.show-header = "enable"
dir-listing.hide-header-file = "enable"
dir-listing.encode-header = "disable"
dir-listing.show-readme = "enable"
dir-listing.hide-readme-file = "enable"
dir-listing.encode-readme = "disable"

...and restart Lighttpd.

Thanks - http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModDirlisting

HOWTO: Allow IP Address To See Directory Listing

$HTTP["host"] == "www.domain.com" {
  $HTTP["remoteip"] =~ "123.456.789.0|987.654.432.1" {
    $HTTP["url"] =~ "^/images/3p($|/)" {
      dir-listing.activate = "enable"
    }
  }
  server.document-root = "/var/www/www.domain.com/html"
  server.errorlog = "/var/www/www.domain.com/logs/error.log"
  accesslog.filename = "/var/www/www.domain.com/logs/access.log"
}


HOWTO: Hide Files From Directory Listing

dir-listing.exclude = ( "favicon.ico" )

HOWTO: Set File Mime Type For Downloads

mimetype.assign += ( ".log" => "text/plain" )

HOWTO: Fix File Timestamps

use_localtime=NO

HOWTO: WebDAV

Install the modules...

sudo apt-get install lighttpd-mod-webdav

Enable the modules...

sudo lighty-enable-mod auth
sudo lighty-enable-mod webdav

Create the directories and apply correct permissions...

sudo mkdir /var/www/domain.co.uk/dav
sudo chown -R www-data:www-data /var/www/domain.co.uk/dav
sudo chmod g+w /var/www/domain.co.uk/dav

Add this to your virtual host file...

 alias.url = ( "/dav" => "/var/www/domain.co.uk/dav" )
 $HTTP["url"] =~ "^/dav($|/)" {
   dir-listing.activate = "enable"
   webdav.activate = "enable"
   webdav.is-readonly = "disable"
   webdav.sqlite-db-name = "/var/run/lighttpd/lighttpd.webdav_lock.db"
   auth.backend = "htpasswd"
   auth.backend.htpasswd.userfile = "/etc/lighttpd/htpasswd"
   auth.require = ( "" => ( "method" => "basic",
                            "realm" => "webdav",
                            "require" => "valid-user" ) )
 }

Restart the web server...

sudo service lighttpd restart

Thanks - https://www.howtoforge.com/how-to-set-up-webdav-with-lighttpd-on-debian-squeeze

HOWTO: Multiple SSL Certificates on Single IP Address using Server Name Indication (SNI)

/etc/lighttpd/lighttpd.conf

In this file, you load the next file.

include "/etc/lighttpd/ssl.conf"

/etc/lighttpd/ssl.conf

In this file, you enable SSL on the port and set a default SSL Certificate then assign different Certificates for each host domain.

Be advised, you still have to include the non-www version of the host domain so that it matches correctly.

This is so you can redirect the non-www version of a domain name to the full subdomain www version - see the section on this below.

$SERVER["socket"] == ":443" {
    ssl.engine  = "enable"
    ssl.pemfile = "/etc/lighttpd/ssl/the-default-domain.com.pem"
    $HTTP["host"] == "example.org" {
        ssl.pemfile = "/etc/lighttpd/example.org.pem"
    }
    $HTTP["host"] == "www.example.org" {
        ssl.pemfile = "/etc/lighttpd/www.example.org.pem"
    }
    $HTTP["host"] == "mail.example.org" {
        ssl.pemfile = "/etc/lighttpd/mail.example.org.pem"
    }
}

HOWTO: Redirect non-www and non-https to https www using schemes

# domain.com

$HTTP["scheme"] == "http" {
  $HTTP["host"] == "domain.com" {
    url.redirect = ( ".*" => "https://www.domain.com/$1" )
    url.redirect-code = 301
  }
  $HTTP["host"] == "www.domain.com" {
    url.redirect = ( ".*" => "https://www.domain.com/$1" )
    url.redirect-code = 301
  }
}

$HTTP["scheme"] == "https" {
  $HTTP["host"] == "domain.com" {
    url.redirect = ( ".*" => "https://www.domain.com/$1" )
    url.redirect-code = 301
  }
  $HTTP["host"] == "www.domain.com" {
    server.name = "www.domain.com"
    server.document-root = "/var/www/www.domain.com/html"
    accesslog.filename = "/var/www/www.domain.com/logs/access.log"
    url.rewrite-if-not-file = ( "^/(wp-.+).*/?" => "$0", "^/keyword/([A-Za-z_0-9\-]+)/?$" => "/index.php?keyword=$1", "^/.*?(\?.*)?$" => "/index.php$1" )
  }
}

HOWTO: Redirect Root Domain To WWW

$HTTP["host"] =~ "^example.com$" {
   url.redirect = (
       "^/(.*)" => "http://www.example.com/$1"
   )
}

HOWTO: Redirect To Secure HTTPS

https://wiki.archlinux.org/index.php/Lighttpd#Redirect_http_requests_to_https

server.modules += ( "mod_redirect" )

$SERVER["socket"] == ":80" {
  $HTTP["host"] =~ "example.org" {
    url.redirect = ( "^/(.*)" => "https://example.org/$1" )
    server.name                 = "example.org" 
  }
}

$SERVER["socket"] == ":443" {
  ssl.engine = "enable" 
  ssl.pemfile = "/etc/lighttpd/certs/server.pem" 
  server.document-root = "..." 
}

When the visitor comes to port 80 (wrapped in the http scheme) it redirects them to port 443 then looks up the host details. This way, you can have multiple host configurations doing different things, like redirecting without any subdomain or a different subdomain with WordPress, and blocking access except for a few IP addresses, etc...

$HTTP["scheme"] == "http" {
  $HTTP["host"] =~ "^secure\.domain\.com$" {
    url.redirect = ( "^/(.*)" => "https://secure.domain.com/$1" )
  }
  $HTTP["host"] =~ "^domain\.com$" {
    url.redirect = ( ".*" => "http://www.domain.com" )
    url.redirect-code = 301
  }
  $HTTP["host"] =~ "^aws\.domain\.com$" {
    server.document-root = "/var/www/aws.domain.com/html"
    server.errorlog = "/var/www/aws.domain.com/logs/error.log"
    accesslog.filename = "/var/www/aws.domain.com/logs/access.log"
    # uncomment below for wordpress
    url.rewrite-if-not-file = ( "^/(wp-.+).*/?" => "$0", "^/keyword/([A-Za-z_0-9\-]+)/?$" => "/index.php?keyword=$1", "^/.*?(\?.*)?$" => "/index.php$1" )
 }
}
$SERVER["socket"] == ":443" {
  ssl.engine = "enable"
  ssl.pemfile = "/etc/lighttpd/domain_com.pem"
  ssl.ca-file = "/etc/lighttpd/BundleCA.crt"
  ssl.use-sslv2 = "disable"
  ssl.use-sslv3 = "disable"
  ssl.cipher-list = "ECDHE-RSA-AES256-SHA384:AES256-SHA256:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM"
  $HTTP["host"] =~ "secure\.domain\.com$" {
    server.document-root = "/var/www/secure.domain.com/html"
    server.errorlog = "/var/www/secure.domain.com/logs/error.log"
    accesslog.filename = "/var/www/secure.domain.com/logs/access.log"
    $HTTP["remoteip"] !~ "123.456.789.10|01.987.654.321|66.0.0.0/8" {
      url.access-deny = ( "" )
    }
  }
}

OLD METHOD

server.modules += ( "mod_redirect" )

$SERVER["socket"] == ":80" {
  $HTTP["host"] =~ "example.org" {
    url.redirect = ( "^/(.*)" => "https://example.org/$1" )
    server.name                 = "example.org" 
  }
}

$SERVER["socket"] == ":443" {
  ssl.engine = "enable" 
  ssl.pemfile = "/etc/lighttpd/ssl/server.pem" 
  server.document-root = "..." 
}

HOWTO: Test Configuration

sudo lighttpd -t -f /etc/lighttpd/lighttpd.conf