Set up Lighttpd web server with SSL certificates from Let's Encrypt and FastCGI support for PHP applications. Configure virtual hosts, optimize performance, and implement security hardening for production hosting.
Prerequisites
- Root or sudo access
- Domain name pointing to your server
- Basic command line knowledge
What this solves
Lighttpd is a lightweight, high-performance web server that uses minimal system resources while delivering fast response times. This tutorial shows you how to install and configure Lighttpd with SSL certificates, FastCGI support for PHP applications, and production-ready security settings for hosting websites efficiently.
Step-by-step installation
Update system packages
Start by updating your package manager to ensure you get the latest versions of all packages.
sudo apt update && sudo apt upgrade -y
Install Lighttpd and dependencies
Install Lighttpd web server along with PHP-FPM for FastCGI support and Certbot for SSL certificates.
sudo apt install -y lighttpd php-fpm php-cli php-mysql php-gd php-curl certbot
Configure firewall rules
Open HTTP and HTTPS ports in the firewall to allow web traffic to reach your server.
sudo ufw allow 'WWW Full'
sudo ufw enable
Configure main Lighttpd settings
Edit the main configuration file to enable modules and set basic server parameters.
server.modules = (
"mod_access",
"mod_alias",
"mod_compress",
"mod_redirect",
"mod_rewrite",
"mod_setenv",
"mod_fastcgi",
"mod_accesslog"
)
server.document-root = "/var/www/html"
server.upload-dirs = ( "/var/cache/lighttpd/uploads" )
server.errorlog = "/var/log/lighttpd/error.log"
server.pid-file = "/var/run/lighttpd.pid"
server.username = "www-data"
server.groupname = "www-data"
server.port = 80
Performance tuning
server.max-connections = 1024
server.max-fds = 2048
server.stat-cache-engine = "simple"
server.max-worker = 4
Security headers
setenv.add-response-header = (
"X-Frame-Options" => "DENY",
"X-Content-Type-Options" => "nosniff",
"X-XSS-Protection" => "1; mode=block",
"Referrer-Policy" => "strict-origin-when-cross-origin"
)
MIME types
mimetype.assign = (
".pdf" => "application/pdf",
".sig" => "application/pgp-signature",
".spl" => "application/futuresplash",
".class" => "application/octet-stream",
".ps" => "application/postscript",
".torrent" => "application/x-bittorrent",
".dvi" => "application/x-dvi",
".gz" => "application/x-gzip",
".pac" => "application/x-ns-proxy-autoconfig",
".swf" => "application/x-shockwave-flash",
".tar.gz" => "application/x-tgz",
".tgz" => "application/x-tgz",
".tar" => "application/x-tar",
".zip" => "application/zip",
".mp3" => "audio/mpeg",
".m3u" => "audio/x-mpegurl",
".wma" => "audio/x-ms-wma",
".wax" => "audio/x-ms-wax",
".ogg" => "application/ogg",
".wav" => "audio/x-wav",
".gif" => "image/gif",
".jpg" => "image/jpeg",
".jpeg" => "image/jpeg",
".png" => "image/png",
".xbm" => "image/x-xbitmap",
".xpm" => "image/x-xpixmap",
".xwd" => "image/x-xwindowdump",
".css" => "text/css",
".html" => "text/html",
".htm" => "text/html",
".js" => "text/javascript",
".asc" => "text/plain",
".c" => "text/plain",
".cpp" => "text/plain",
".log" => "text/plain",
".conf" => "text/plain",
".text" => "text/plain",
".txt" => "text/plain",
".dtd" => "text/xml",
".xml" => "text/xml",
".mpeg" => "video/mpeg",
".mpg" => "video/mpeg",
".mov" => "video/quicktime",
".qt" => "video/quicktime",
".avi" => "video/x-msvideo",
".asf" => "video/x-ms-asf",
".asx" => "video/x-ms-asf",
".wmv" => "video/x-ms-wmv",
".bz2" => "application/x-bzip",
".tbz" => "application/x-bzip-compressed-tar",
".tar.bz2" => "application/x-bzip-compressed-tar",
"" => "application/octet-stream"
)
Configure PHP FastCGI
Set up FastCGI to handle PHP requests efficiently through PHP-FPM.
fastcgi.server += ( ".php" =>
((
"socket" => "/var/run/php/php-fpm.sock",
"broken-scriptfilename" => "enable"
))
)
Enable FastCGI configuration
Enable the FastCGI PHP configuration module.
sudo lighttpd-enable-mod fastcgi-php
Configure virtual host
Create a virtual host configuration for your domain. Replace example.com with your actual domain name.
$HTTP["host"] == "example.com" {
server.document-root = "/var/www/example.com/public"
accesslog.filename = "/var/log/lighttpd/example.com-access.log"
# PHP FastCGI for this virtual host
fastcgi.server = ( ".php" =>
((
"socket" => "/var/run/php/php-fpm.sock",
"broken-scriptfilename" => "enable"
))
)
# Security settings
$HTTP["url"] =~ "^/(\.|_)" {
url.access-deny = ("")
}
# Cache static files
$HTTP["url"] =~ "\.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2)$" {
expire.url = ( "" => "access plus 1 months" )
}
}
Create document root and test page
Create the directory structure for your website and a test PHP file.
sudo mkdir -p /var/www/example.com/public
sudo chown -R www-data:www-data /var/www/example.com
sudo chmod -R 755 /var/www/example.com
Enable virtual host configuration
Enable the virtual host configuration and restart Lighttpd to apply changes.
sudo ln -s /etc/lighttpd/conf-available/90-example.conf /etc/lighttpd/conf-enabled/
sudo systemctl restart lighttpd
sudo systemctl enable lighttpd
Obtain SSL certificate with Let's Encrypt
Use Certbot to obtain a free SSL certificate for your domain. Replace example.com with your actual domain.
sudo certbot certonly --webroot -w /var/www/example.com/public -d example.com -d www.example.com
Configure SSL in Lighttpd
Enable SSL module and configure HTTPS with the Let's Encrypt certificate.
sudo lighttpd-enable-mod ssl
$HTTP["scheme"] == "http" {
$HTTP["host"] =~ ".*" {
url.redirect = (".*" => "https://%0$0")
}
}
$SERVER["socket"] == ":443" {
ssl.engine = "enable"
ssl.pemfile = "/etc/ssl/private/lighttpd-combined.pem"
ssl.ca-file = "/etc/ssl/certs/ca-certificates.crt"
# Modern SSL configuration
ssl.cipher-list = "ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384"
ssl.honor-cipher-order = "enable"
ssl.disable-client-renegotiation = "enable"
}
Create combined SSL certificate file
Lighttpd requires the private key and certificate in a single file.
sudo cat /etc/letsencrypt/live/example.com/privkey.pem /etc/letsencrypt/live/example.com/cert.pem > /etc/ssl/private/lighttpd-combined.pem
sudo chmod 600 /etc/ssl/private/lighttpd-combined.pem
sudo chown root:root /etc/ssl/private/lighttpd-combined.pem
Configure automatic certificate renewal
Create a renewal hook to recreate the combined certificate file when Let's Encrypt renews certificates.
#!/bin/bash
cat /etc/letsencrypt/live/example.com/privkey.pem /etc/letsencrypt/live/example.com/cert.pem > /etc/ssl/private/lighttpd-combined.pem
chmod 600 /etc/ssl/private/lighttpd-combined.pem
systemctl reload lighttpd
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/lighttpd-reload.sh
Configure compression and caching
Enable gzip compression and browser caching to improve performance.
compress.cache-dir = "/var/cache/lighttpd/compress/"
compress.filetype = (
"application/javascript",
"application/x-javascript",
"application/json",
"application/xml",
"text/css",
"text/html",
"text/javascript",
"text/plain",
"text/xml"
)
compress.max-filesize = 2048
sudo mkdir -p /var/cache/lighttpd/compress
sudo chown www-data:www-data /var/cache/lighttpd/compress
sudo lighttpd-enable-mod compress
Restart Lighttpd with SSL enabled
Restart the web server to apply all SSL and performance configurations.
sudo systemctl restart lighttpd
sudo systemctl status lighttpd
Configure log rotation
Set up log rotation to prevent log files from consuming excessive disk space, similar to monitoring setups covered in our disk usage monitoring tutorial.
/var/log/lighttpd/*.log {
daily
missingok
rotate 52
compress
notifempty
sharedscripts
postrotate
if [ -f /var/run/lighttpd.pid ]; then
/bin/kill -USR1 cat /var/run/lighttpd.pid
fi
endscript
}
Performance tuning
Optimize system limits
Increase system limits for better performance under load, building on concepts from system performance optimization.
www-data soft nofile 65536
www-data hard nofile 65536
Configure PHP-FPM optimization
Optimize PHP-FPM settings for better performance with Lighttpd.
[www]
user = www-data
group = www-data
listen = /var/run/php/php-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.max_requests = 500
php_admin_value[memory_limit] = 256M
php_admin_value[max_execution_time] = 30
php_admin_value[upload_max_filesize] = 10M
php_admin_value[post_max_size] = 10M
Restart services
Restart both PHP-FPM and Lighttpd to apply performance optimizations.
sudo systemctl restart php8.3-fpm
sudo systemctl restart lighttpd
Verify your setup
Test your Lighttpd installation and configuration with these verification commands.
sudo systemctl status lighttpd
sudo systemctl status php-fpm
sudo lighttpd -t -f /etc/lighttpd/lighttpd.conf
curl -I https://example.com
ssl-cert-check -c /etc/ssl/private/lighttpd-combined.pem
Security hardening
Hide server information
Configure Lighttpd to hide version information and server details from HTTP headers.
server.tag = ""
server.server-root = "/var/www"
Deny access to sensitive files
$HTTP["url"] =~ "/(\.|~)" {
url.access-deny = ("")
}
Deny access to backup files
$HTTP["url"] =~ "\.(bak|config|dist|fla|inc|ini|log|psd|sh|sql|swp)$" {
url.access-deny = ("")
}
Rate limiting
connection.kbytes-per-second = 0
server.kbytes-per-second = 0
sudo ln -s /etc/lighttpd/conf-available/10-security.conf /etc/lighttpd/conf-enabled/
Configure fail2ban protection
Set up fail2ban to protect against brute force attacks on your web server.
sudo apt install -y fail2ban
[lighttpd-auth]
enabled = true
port = http,https
filter = lighttpd-auth
logpath = /var/log/lighttpd/error.log
maxretry = 3
bantime = 3600
sudo systemctl enable --now fail2ban
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| 403 Forbidden error | Incorrect file permissions | sudo chown -R www-data:www-data /var/www && sudo chmod -R 755 /var/www |
| PHP files download instead of executing | FastCGI not configured properly | Check FastCGI configuration and restart sudo systemctl restart php-fpm lighttpd |
| SSL certificate errors | Combined certificate file missing or incorrect | Recreate combined certificate: sudo cat /etc/letsencrypt/live/example.com/privkey.pem /etc/letsencrypt/live/example.com/cert.pem > /etc/ssl/private/lighttpd-combined.pem |
| Server won't start | Configuration syntax error | Test configuration: sudo lighttpd -t -f /etc/lighttpd/lighttpd.conf |
| High memory usage | Too many PHP-FPM processes | Adjust pm.max_children in PHP-FPM pool configuration |
Next steps
- Setup nginx reverse proxy with SSL certificates for more advanced reverse proxy configurations
- Configure centralized log management to better monitor your Lighttpd server
- Configure Lighttpd with multiple virtual hosts and subdomains
- Implement Lighttpd load balancing with multiple backend servers
- Configure Lighttpd with Redis caching for high-performance websites
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# Default values
DOMAIN=""
EMAIL=""
# Usage function
usage() {
echo "Usage: $0 -d DOMAIN -e EMAIL"
echo " -d DOMAIN Domain name for SSL certificate"
echo " -e EMAIL Email for SSL certificate registration"
exit 1
}
# Parse arguments
while getopts "d:e:h" opt; do
case $opt in
d) DOMAIN="$OPTARG" ;;
e) EMAIL="$OPTARG" ;;
h) usage ;;
*) usage ;;
esac
done
# Validate required arguments
if [[ -z "$DOMAIN" || -z "$EMAIL" ]]; then
echo -e "${RED}Error: Domain and email are required${NC}"
usage
fi
# Check if running as root or with sudo
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}Error: This script must be run as root or with sudo${NC}"
exit 1
fi
# Cleanup function for rollback
cleanup() {
echo -e "${RED}Error occurred. Cleaning up...${NC}"
systemctl stop lighttpd 2>/dev/null || true
systemctl stop php-fpm 2>/dev/null || true
}
trap cleanup ERR
# Auto-detect distribution
if [[ ! -f /etc/os-release ]]; then
echo -e "${RED}Error: Cannot detect distribution${NC}"
exit 1
fi
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_UPDATE="apt update && apt upgrade -y"
PKG_INSTALL="apt install -y"
LIGHTTPD_CONF="/etc/lighttpd/lighttpd.conf"
LIGHTTPD_USER="www-data"
LIGHTTPD_GROUP="www-data"
PHP_FPM_POOL="/etc/php/*/fpm/pool.d/www.conf"
FIREWALL_CMD="ufw"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
LIGHTTPD_CONF="/etc/lighttpd/lighttpd.conf"
LIGHTTPD_USER="lighttpd"
LIGHTTPD_GROUP="lighttpd"
PHP_FPM_POOL="/etc/php-fpm.d/www.conf"
FIREWALL_CMD="firewalld"
;;
amzn)
PKG_MGR="yum"
PKG_UPDATE="yum update -y"
PKG_INSTALL="yum install -y"
LIGHTTPD_CONF="/etc/lighttpd/lighttpd.conf"
LIGHTTPD_USER="lighttpd"
LIGHTTPD_GROUP="lighttpd"
PHP_FPM_POOL="/etc/php-fpm.d/www.conf"
FIREWALL_CMD="firewalld"
;;
*)
echo -e "${RED}Error: Unsupported distribution: $ID${NC}"
exit 1
;;
esac
echo -e "${BLUE}Installing Lighttpd with SSL and FastCGI on $PRETTY_NAME${NC}"
# Step 1: Update system
echo -e "${GREEN}[1/8] Updating system packages...${NC}"
$PKG_UPDATE
# Step 2: Install packages
echo -e "${GREEN}[2/8] Installing Lighttpd and dependencies...${NC}"
if [[ "$ID" == "ubuntu" || "$ID" == "debian" ]]; then
$PKG_INSTALL lighttpd php-fpm php-cli php-mysql php-gd php-curl certbot
else
$PKG_INSTALL lighttpd php-fpm php-cli php-mysqlnd php-gd php-curl certbot
# Enable EPEL if needed for certbot
if [[ "$ID" == "centos" || "$ID" == "rhel" || "$ID" == "almalinux" || "$ID" == "rocky" ]]; then
$PKG_INSTALL epel-release || true
fi
fi
# Step 3: Configure firewall
echo -e "${GREEN}[3/8] Configuring firewall...${NC}"
if [[ "$FIREWALL_CMD" == "ufw" ]]; then
ufw --force enable
ufw allow 'WWW Full'
else
systemctl enable --now firewalld
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
fi
# Step 4: Create necessary directories
echo -e "${GREEN}[4/8] Creating directories and setting permissions...${NC}"
mkdir -p /var/www/html
mkdir -p /var/cache/lighttpd/uploads
mkdir -p /var/log/lighttpd
chown -R $LIGHTTPD_USER:$LIGHTTPD_GROUP /var/www/html
chown -R $LIGHTTPD_USER:$LIGHTTPD_GROUP /var/cache/lighttpd
chown -R $LIGHTTPD_USER:$LIGHTTPD_GROUP /var/log/lighttpd
chmod 755 /var/www/html
chmod 755 /var/cache/lighttpd/uploads
# Step 5: Configure Lighttpd
echo -e "${GREEN}[5/8] Configuring Lighttpd...${NC}"
cat > $LIGHTTPD_CONF << 'EOF'
server.modules = (
"mod_access",
"mod_alias",
"mod_compress",
"mod_redirect",
"mod_rewrite",
"mod_setenv",
"mod_fastcgi",
"mod_accesslog"
)
server.document-root = "/var/www/html"
server.upload-dirs = ( "/var/cache/lighttpd/uploads" )
server.errorlog = "/var/log/lighttpd/error.log"
server.pid-file = "/var/run/lighttpd.pid"
server.port = 80
# Performance tuning
server.max-connections = 1024
server.max-fds = 2048
server.stat-cache-engine = "simple"
server.max-worker = 4
# Security headers
setenv.add-response-header = (
"X-Frame-Options" => "DENY",
"X-Content-Type-Options" => "nosniff",
"X-XSS-Protection" => "1; mode=block",
"Referrer-Policy" => "strict-origin-when-cross-origin"
)
# MIME types
mimetype.assign = (
".pdf" => "application/pdf",
".class" => "application/octet-stream",
".gz" => "application/x-gzip",
".tar" => "application/x-tar",
".zip" => "application/zip",
".mp3" => "audio/mpeg",
".ogg" => "application/ogg",
".wav" => "audio/x-wav",
".gif" => "image/gif",
".jpg" => "image/jpeg",
".jpeg" => "image/jpeg",
".png" => "image/png",
".css" => "text/css",
".html" => "text/html",
".htm" => "text/html",
".js" => "text/javascript",
".txt" => "text/plain",
".xml" => "text/xml",
".mpeg" => "video/mpeg",
".mpg" => "video/mpeg",
".mov" => "video/quicktime",
".avi" => "video/x-msvideo"
)
# FastCGI for PHP
fastcgi.server = ( ".php" => ((
"socket" => "/var/run/php-fpm.sock",
"broken-scriptfilename" => "enable"
)))
index-file.names = ( "index.php", "index.html" )
EOF
# Set lighttpd user in config
sed -i "s/server.username.*/server.username = \"$LIGHTTPD_USER\"/" $LIGHTTPD_CONF
sed -i "s/server.groupname.*/server.groupname = \"$LIGHTTPD_GROUP\"/" $LIGHTTPD_CONF
# Step 6: Configure PHP-FPM
echo -e "${GREEN}[6/8] Configuring PHP-FPM...${NC}"
PHP_FPM_CONF=$(find /etc -name "www.conf" -path "*/php*fpm*" 2>/dev/null | head -1)
if [[ -n "$PHP_FPM_CONF" ]]; then
sed -i "s/listen = .*/listen = \/var\/run\/php-fpm.sock/" "$PHP_FPM_CONF"
sed -i "s/;listen.owner = .*/listen.owner = $LIGHTTPD_USER/" "$PHP_FPM_CONF"
sed -i "s/;listen.group = .*/listen.group = $LIGHTTPD_GROUP/" "$PHP_FPM_CONF"
sed -i "s/;listen.mode = .*/listen.mode = 0660/" "$PHP_FPM_CONF"
fi
# Step 7: Create test page
echo -e "${GREEN}[7/8] Creating test page...${NC}"
cat > /var/www/html/index.html << EOF
<!DOCTYPE html>
<html>
<head>
<title>Lighttpd Test Page</title>
</head>
<body>
<h1>Lighttpd is working!</h1>
<p>Server: $(hostname)</p>
<p>Domain: $DOMAIN</p>
</body>
</html>
EOF
cat > /var/www/html/info.php << 'EOF'
<?php
phpinfo();
?>
EOF
chown -R $LIGHTTPD_USER:$LIGHTTPD_GROUP /var/www/html
chmod 644 /var/www/html/*.html /var/www/html/*.php
# Start services
systemctl enable --now lighttpd
systemctl enable --now php-fpm
# Step 8: SSL Configuration
echo -e "${GREEN}[8/8] Configuring SSL certificate...${NC}"
if certbot certonly --webroot --webroot-path=/var/www/html -d $DOMAIN --email $EMAIL --agree-tos --non-interactive; then
# Create SSL configuration
cat >> $LIGHTTPD_CONF << EOF
# SSL Configuration
\$SERVER["socket"] == ":443" {
ssl.engine = "enable"
ssl.pemfile = "/etc/letsencrypt/live/$DOMAIN/privkey.pem"
ssl.ca-file = "/etc/letsencrypt/live/$DOMAIN/fullchain.pem"
ssl.cipher-list = "HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!SRP:!CAMELLIA"
ssl.honor-cipher-order = "enable"
ssl.disable-client-renegotiation = "enable"
}
# Redirect HTTP to HTTPS
\$HTTP["scheme"] == "http" {
url.redirect = (".*" => "https://%s\$0")
}
EOF
systemctl restart lighttpd
echo -e "${GREEN}SSL certificate installed successfully${NC}"
else
echo -e "${YELLOW}Warning: SSL certificate installation failed. Server will run on HTTP only${NC}"
fi
# Verification
echo -e "${BLUE}Verifying installation...${NC}"
sleep 3
if systemctl is-active --quiet lighttpd; then
echo -e "${GREEN}✓ Lighttpd is running${NC}"
else
echo -e "${RED}✗ Lighttpd is not running${NC}"
exit 1
fi
if systemctl is-active --quiet php-fpm; then
echo -e "${GREEN}✓ PHP-FPM is running${NC}"
else
echo -e "${RED}✗ PHP-FPM is not running${NC}"
exit 1
fi
if curl -s -o /dev/null -w "%{http_code}" http://localhost | grep -q "200"; then
echo -e "${GREEN}✓ Web server is responding${NC}"
else
echo -e "${YELLOW}⚠ Web server may not be responding correctly${NC}"
fi
echo -e "${GREEN}Installation completed successfully!${NC}"
echo -e "${BLUE}Access your site at: http://$DOMAIN${NC}"
if [[ -f "/etc/letsencrypt/live/$DOMAIN/fullchain.pem" ]]; then
echo -e "${BLUE}HTTPS access: https://$DOMAIN${NC}"
fi
echo -e "${BLUE}PHP info page: http://$DOMAIN/info.php${NC}"
echo -e "${YELLOW}Remember to remove info.php in production!${NC}"
Review the script before running. Execute with: bash install.sh