As the name implies, the main advantage of lighttpd over Apache2 and Nginx is the lower resource usage, making it suitable for handling high traffic with less memory and CPU usage.

Because of its weight, it is the recommended web server t run on a Raspberry Pi Zero, even though any of them would work but the performance might vary.


INSTALLING WEB SERVER PLUS PHP

sudo apt update && sudo apt upgrade -y
sudo apt install lighttpd -y
sudo apt install php7.4 php7.4-fpm php7.4-mysql php7.4-cli php7.4-curl php7.4-xml curl -y
sudo nano /etc/php/7.4/fpm/pool.d/www.conf
;listen = /run/php/php7.4-fpm.sock
listen = 127.0.0.1:9000
                #"bin-path" => "/usr/bin/php-cgi",
                #"socket" => "/var/run/lighttpd/php.socket",
                "host" => "127.0.0.1",
                "port" => "9000",
sudo lighty-enable-mod fastcgi
sudo lighty-enable-mod fastcgi-php
sudo systemctl restart lighttpd php7.4-fpm
rm /var/www/html/index.lighttpd.html
echo '<?php echo "HTTP_HOST: " . $_SERVER["HTTP_HOST"] . " SERVER_NAME: " . $_SERVER["SERVER_NAME"] ?>' | sudo tee /var/www/html/index.php
sudo chown -R www-data: /var/www/html/ && sudo chmod -R 755 /var/www/html/

At this point, any HTTP request to this server will be server with the same page. Consider using Virtual Hosts to server multiple sites based on its Host header.


SETTING A VIRTUAL HOST

sudo nano /etc/lighttpd/lighttpd.conf
...
include_shell "/usr/share/lighttpd/create-mime.conf.pl"
include_shell "cat /etc/lighttpd/vhosts.d/*.conf"
include "/etc/lighttpd/conf-enabled/*.conf"
...
sudo mkdir -p /etc/lighttpd/vhosts.d/
sudo nano /etc/lighttpd/vhosts.d/localhost.conf
$HTTP["host"] =~ "(^|.)localhost$" {
    server.document-root = "/var/www/html/localhost"
}
sudo mkdir -p /var/www/html/localhost
echo 'VHOST: localhost' | sudo tee /var/www/html/localhost/index.html
sudo lighttpd -t -f /etc/lighttpd/lighttpd.conf && sudo systemctl restart lighttpd.service
curl http://127.0.0.1
curl http://localhost

While the first request is served with the default site, the second request will be server with a different content.


DEPLOYING A SELF-SIGNED CERTIFICATE

Replace the fake domain example.com accordingly.

sudo mkdir -p /etc/lighttpd/ssl/example.com
cd /etc/lighttpd/ssl/example.com
sudo openssl req -new -x509 -keyout server.pem -out server.pem -days 365 -nodes
sudo nano /etc/lighttpd/conf-enabled/10-ssl.conf
# /usr/share/doc/lighttpd/ssl.txt
server.modules += ( "mod_openssl" )
$HTTP["scheme"] == "http" {
    $HTTP["host"] =~ ".*" {
        url.redirect = (".*" => "https://%0$0")
    }
}
$SERVER["socket"] == "0.0.0.0:443" {
        ssl.engine  = "enable"
        ssl.pemfile = "/etc/lighttpd/ssl/domain.com/server.pem"
        ssl.cipher-list = "HIGH"
}
sudo lighttpd -t -f /etc/lighttpd/lighttpd.conf && sudo systemctl restart lighttpd.service

ISSUING A FREE PUBLIC CERTIFICATE

Replace the fake domain example.com with a real public domain that has a record that points to the webs server.

add-apt-repository ppa:certbot/certbot && sudo apt install certbot -y
sudo lighttpd-enable-mod ssl
sudo certbot certonly --webroot -w /var/www/html -d example.com -d www.example.com
ls -l /etc/letsencrypt/live/example.com
sudo nano /etc/lighttpd/lighttpd.conf
# /usr/share/doc/lighttpd/ssl.txt
server.modules += ( "mod_openssl" )
$HTTP["scheme"] == "http" {
    $HTTP["host"] =~ ".*" {
        url.redirect = (".*" => "https://%0$0")
    }
}
$SERVER["socket"] == "0.0.0.0:443" {
        ssl.engine  = "enable"
        ssl.pemfile = "/etc/letsencrypt/live/example.com/fullchain.pem"
        ssl.privkey = "/etc/letsencrypt/live/example.com/privkey.pem"
        ssl.cipher-list = "HIGH"
}
sudo lighttpd -t -f /etc/lighttpd/lighttpd.conf && sudo systemctl restart lighttpd.service

RENEWING THE PUBLIC CERTIFICATE

Certbot (that used Let’s Encrypt) does not automatically renew he free 90 days public certificates.

Follows the manual renewal command that can be easily scheduled to periodically happens as a cronjob:

sudo certbot renew --force-renewal -d example.com -d www.example.com

BONUS

Reverse proxy for a VHOST:

$HTTP["host"] =~ "(^|.)example.com$" {
    proxy.server = ( "" => ( ( "host" => "127.0.0.1", "port" => 8080 ) ) )
}

Reverse proxy for a PATH rewriting the HEADER:

$HTTP["url"] =~ "(^/path)" {
    proxy.header = ( "map-urlpath" => ( "/path" => "/") )
    proxy.server = ( "" => ( ( "host" => "127.0.0.1", "port" => 8080 ) ) )
}

Another way of creating a self-signed certificate would be:

sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout /etc/ssl/private/apache-selfsigned.key -out /etc/ssl/certs/apache-selfsigned.crt

Note: the different file output format.


REFLECTIONS

Lighttpd is a powerful lightweight web server very suitable to run with minimal resources. If that is the case, I would recommend to keep it behind a reverse proxy that terminates the SSL/TLS certificate to offload the server. A CDN provider, such as Cloud Flare, would not only serve as a shield for many types threats but would also terminate the encrypted connections.

Do not forget to enable and minimise the firewall rules to the bare minimum necessary. If for instance a CDN is used, whitelist connections to restricts connections only from the CDN vendor’s network.

For web sites that contains sensitive information or requires end-to-end encryption because it uses passwords, for example, it is recommended to at least offer a self-signed certificate.