Configure Lighttpd 1.4 with multiple virtual hosts and subdomains

Intermediate 45 min Apr 15, 2026 179 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up Lighttpd web server with multiple virtual hosts, subdomain routing, SSL certificates, and performance optimization for hosting multiple websites on a single server with security hardening.

Prerequisites

  • Root or sudo access to the server
  • Domain names pointing to your server IP address
  • Basic knowledge of DNS configuration
  • Familiarity with command line operations

What this solves

Virtual hosts allow you to serve multiple websites from a single Lighttpd server, each with its own domain name and configuration. This tutorial shows you how to configure domain-based virtual hosting, subdomain routing, SSL certificates, and security hardening for production environments.

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
sudo dnf update -y

Install Lighttpd and required modules

Install Lighttpd web server along with SSL support and additional modules needed for virtual hosts and subdomains.

sudo apt install -y lighttpd lighttpd-mod-openssl openssl certbot
sudo dnf install -y lighttpd lighttpd-mod_openssl openssl certbot

Create directory structure for virtual hosts

Set up organized directory structure for each virtual host with proper ownership and permissions.

sudo mkdir -p /var/www/example.com/{public_html,logs}
sudo mkdir -p /var/www/api.example.com/{public_html,logs}
sudo mkdir -p /var/www/blog.example.com/{public_html,logs}
sudo mkdir -p /var/www/testsite.org/{public_html,logs}

Set proper ownership and permissions

Configure correct file ownership and permissions for web content. The www-data user needs read access to serve files, and the lighttpd process must be able to write to log directories.

sudo chown -R www-data:www-data /var/www/
sudo chmod -R 755 /var/www/*/public_html
sudo chmod -R 775 /var/www/*/logs
Never use chmod 777. It gives every user on the system full access to your files. Instead, use specific ownership with chown and minimal permissions like 755 for directories and 644 for files.

Create sample HTML files for testing

Create simple HTML files to test each virtual host configuration.

sudo tee /var/www/example.com/public_html/index.html > /dev/null << 'EOF'


Main Site - example.com

Welcome to example.com

Main website content

EOF sudo tee /var/www/api.example.com/public_html/index.html > /dev/null << 'EOF' API - api.example.com

API Subdomain

API endpoint at api.example.com

EOF sudo tee /var/www/blog.example.com/public_html/index.html > /dev/null << 'EOF' Blog - blog.example.com

Blog Subdomain

Blog content at blog.example.com

EOF sudo tee /var/www/testsite.org/public_html/index.html > /dev/null << 'EOF' Test Site - testsite.org

Welcome to testsite.org

Secondary domain content

EOF

Configure main Lighttpd settings

Backup the original configuration and create a new main configuration file with virtual host support enabled.

sudo cp /etc/lighttpd/lighttpd.conf /etc/lighttpd/lighttpd.conf.backup
server.modules = (
    "mod_access",
    "mod_alias",
    "mod_compress",
    "mod_redirect",
    "mod_rewrite",
    "mod_openssl",
    "mod_accesslog",
    "mod_expire",
    "mod_setenv"
)

Basic server settings

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 optimization

server.max-connections = 1024 server.max-fds = 2048 server.network-backend = "linux-sendfile" server.max-request-size = 16384

MIME types

include "/etc/lighttpd/mime-types.conf"

Default index files

index-file.names = ("index.php", "index.html", "index.htm", "default.htm") url.access-deny = ("~", ".inc")

Compression

compress.cache-dir = "/var/cache/lighttpd/compress/" compress.filetype = ("application/javascript", "text/css", "text/html", "text/plain")

Security headers

setenv.add-response-header = ( "X-Frame-Options" => "SAMEORIGIN", "X-Content-Type-Options" => "nosniff", "X-XSS-Protection" => "1; mode=block", "Referrer-Policy" => "strict-origin-when-cross-origin" )

Include virtual host configurations

include "/etc/lighttpd/vhosts.d/*.conf"

Create virtual hosts directory

Create a dedicated directory for virtual host configuration files to keep the main configuration organized.

sudo mkdir -p /etc/lighttpd/vhosts.d

Configure primary domain virtual host

Create the virtual host configuration for the main domain with logging and error handling.

# Main domain: example.com
$HTTP["host"] == "example.com" {
    server.document-root = "/var/www/example.com/public_html"
    accesslog.filename = "/var/www/example.com/logs/access.log"
    server.errorlog = "/var/www/example.com/logs/error.log"
    
    # Custom error pages
    server.error-handler-404 = "/404.html"
    
    # Cache control for static assets
    $HTTP["url"] =~ "\.(css|js|png|jpg|jpeg|gif|ico|svg)$" {
        expire.url = ("" => "access plus 1 months")
    }
}

Redirect www to non-www

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

Configure subdomain virtual hosts

Set up virtual host configurations for API and blog subdomains with specific optimizations for each use case.

# API subdomain: api.example.com
$HTTP["host"] == "api.example.com" {
    server.document-root = "/var/www/api.example.com/public_html"
    accesslog.filename = "/var/www/api.example.com/logs/access.log"
    server.errorlog = "/var/www/api.example.com/logs/error.log"
    
    # API-specific headers
    setenv.add-response-header = (
        "Access-Control-Allow-Origin" => "https://example.com",
        "Access-Control-Allow-Methods" => "GET, POST, PUT, DELETE, OPTIONS",
        "Access-Control-Allow-Headers" => "Content-Type, Authorization",
        "Content-Type" => "application/json; charset=utf-8"
    )
    
    # No caching for API responses
    setenv.add-response-header += (
        "Cache-Control" => "no-cache, no-store, must-revalidate",
        "Pragma" => "no-cache",
        "Expires" => "0"
    )
}

Blog subdomain: blog.example.com

$HTTP["host"] == "blog.example.com" { server.document-root = "/var/www/blog.example.com/public_html" accesslog.filename = "/var/www/blog.example.com/logs/access.log" server.errorlog = "/var/www/blog.example.com/logs/error.log" # Blog-optimized caching $HTTP["url"] =~ "\.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2)$" { expire.url = ("" => "access plus 3 months") } # HTML caching for blog posts $HTTP["url"] =~ "\.html$" { expire.url = ("" => "access plus 1 hours") } }

Configure secondary domain

Set up a completely separate domain with its own virtual host configuration.

# Secondary domain: testsite.org
$HTTP["host"] == "testsite.org" {
    server.document-root = "/var/www/testsite.org/public_html"
    accesslog.filename = "/var/www/testsite.org/logs/access.log"
    server.errorlog = "/var/www/testsite.org/logs/error.log"
    
    # Different security headers for this domain
    setenv.add-response-header = (
        "X-Frame-Options" => "DENY",
        "Strict-Transport-Security" => "max-age=31536000; includeSubDomains"
    )
}

Handle www redirect for secondary domain

$HTTP["host"] == "www.testsite.org" { url.redirect = ("^/(.*)" => "https://testsite.org/$1") }

Configure SSL certificates

Set up SSL certificates using Let's Encrypt for secure HTTPS connections. First, test the configuration and start Lighttpd temporarily.

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

Obtain SSL certificates for all domains and subdomains:

sudo certbot certonly --webroot -w /var/www/example.com/public_html -d example.com
sudo certbot certonly --webroot -w /var/www/api.example.com/public_html -d api.example.com
sudo certbot certonly --webroot -w /var/www/blog.example.com/public_html -d blog.example.com
sudo certbot certonly --webroot -w /var/www/testsite.org/public_html -d testsite.org

Configure SSL virtual hosts

Create SSL-enabled virtual host configurations with HTTPS redirects and security headers.

# SSL Configuration
$SERVER["socket"] == ":443" {
    ssl.engine = "enable"
    ssl.pemfile = "/etc/letsencrypt/live/example.com/privkey.pem"
    ssl.ca-file = "/etc/letsencrypt/live/example.com/fullchain.pem"
    
    # Modern SSL configuration
    ssl.use-sslv2 = "disable"
    ssl.use-sslv3 = "disable"
    ssl.cipher-list = "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"
    ssl.honor-cipher-order = "enable"
    
    # example.com SSL
    $HTTP["host"] == "example.com" {
        ssl.pemfile = "/etc/letsencrypt/live/example.com/privkey.pem"
        ssl.ca-file = "/etc/letsencrypt/live/example.com/fullchain.pem"
        server.document-root = "/var/www/example.com/public_html"
        accesslog.filename = "/var/www/example.com/logs/ssl_access.log"
        
        setenv.add-response-header = (
            "Strict-Transport-Security" => "max-age=31536000; includeSubDomains; preload"
        )
    }
    
    # API subdomain SSL
    $HTTP["host"] == "api.example.com" {
        ssl.pemfile = "/etc/letsencrypt/live/api.example.com/privkey.pem"
        ssl.ca-file = "/etc/letsencrypt/live/api.example.com/fullchain.pem"
        server.document-root = "/var/www/api.example.com/public_html"
        accesslog.filename = "/var/www/api.example.com/logs/ssl_access.log"
    }
    
    # Blog subdomain SSL
    $HTTP["host"] == "blog.example.com" {
        ssl.pemfile = "/etc/letsencrypt/live/blog.example.com/privkey.pem"
        ssl.ca-file = "/etc/letsencrypt/live/blog.example.com/fullchain.pem"
        server.document-root = "/var/www/blog.example.com/public_html"
        accesslog.filename = "/var/www/blog.example.com/logs/ssl_access.log"
    }
    
    # Secondary domain SSL
    $HTTP["host"] == "testsite.org" {
        ssl.pemfile = "/etc/letsencrypt/live/testsite.org/privkey.pem"
        ssl.ca-file = "/etc/letsencrypt/live/testsite.org/fullchain.pem"
        server.document-root = "/var/www/testsite.org/public_html"
        accesslog.filename = "/var/www/testsite.org/logs/ssl_access.log"
    }
}

Force HTTPS redirects

$HTTP["scheme"] == "http" { $HTTP["host"] =~ ".*" { url.redirect = (".*" => "https://%0$0") } }

Configure automatic certificate renewal

Set up automatic SSL certificate renewal with Lighttpd reload to prevent service interruption.

sudo crontab -e

Add this line to the crontab:

0 2   * /usr/bin/certbot renew --quiet --post-hook "systemctl reload lighttpd"

Configure log rotation

Set up log rotation to prevent log files from consuming too much disk space.

/var/www//logs/.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    create 644 www-data www-data
    postrotate
        if systemctl is-active lighttpd > /dev/null; then
            systemctl reload lighttpd
        fi
    endscript
}

Enable and start Lighttpd

Test the configuration and enable the service to start automatically on boot.

sudo lighttpd -t -f /etc/lighttpd/lighttpd.conf
sudo systemctl enable lighttpd
sudo systemctl restart lighttpd
sudo systemctl status lighttpd

Configure firewall rules

Open the necessary ports for HTTP and HTTPS traffic through the firewall.

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw reload
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload

Verify your setup

Test each virtual host to ensure proper configuration and functionality.

# Check Lighttpd configuration
sudo lighttpd -t -f /etc/lighttpd/lighttpd.conf

Verify service status

sudo systemctl status lighttpd

Test HTTP responses (replace with your actual domains)

curl -H "Host: example.com" http://localhost/ curl -H "Host: api.example.com" http://localhost/ curl -H "Host: blog.example.com" http://localhost/ curl -H "Host: testsite.org" http://localhost/

Test HTTPS redirects

curl -I -H "Host: example.com" http://localhost/

Check SSL certificate status

sudo certbot certificates

Verify log files are being created

sudo ls -la /var/www/*/logs/

Test log rotation configuration

sudo logrotate -d /etc/logrotate.d/lighttpd-vhosts

Performance optimization

Enable advanced caching

Configure advanced caching strategies for better performance across all virtual hosts.

# Advanced caching configuration
$HTTP["url"] =~ "\.(css|js)$" {
    expire.url = ("" => "access plus 1 months")
    setenv.add-response-header = ("Cache-Control" => "public, max-age=2592000")
}

$HTTP["url"] =~ "\.(png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$" {
    expire.url = ("" => "access plus 6 months")
    setenv.add-response-header = ("Cache-Control" => "public, max-age=15552000")
}

Enable compression for text files

compress.filetype = ( "text/plain", "text/html", "text/css", "text/javascript", "application/javascript", "application/json", "application/xml", "text/xml" )

Enable the caching configuration:

sudo ln -s /etc/lighttpd/conf-available/10-cache.conf /etc/lighttpd/conf-enabled/
sudo systemctl reload lighttpd

Optimize worker processes

Configure Lighttpd for optimal performance based on server resources. You can integrate this with Redis caching for even better performance.

# Performance tuning
server.max-connections = 2048
server.max-fds = 4096
server.max-request-size = 8192

Connection handling

server.max-keep-alive-requests = 100 server.max-keep-alive-idle = 30 server.max-read-idle = 60 server.max-write-idle = 360

Network optimization

server.network-backend = "linux-sendfile" server.upload-dirs = ("/tmp")

Memory limits

server.max-request-field-size = 8192 server.max-request-fields = 100
sudo ln -s /etc/lighttpd/conf-available/15-performance.conf /etc/lighttpd/conf-enabled/
sudo systemctl reload lighttpd

Security hardening

Configure additional security headers

Add comprehensive security headers to protect against common web attacks.

# Security headers for all virtual hosts
setenv.add-response-header = (
    "X-Frame-Options" => "SAMEORIGIN",
    "X-Content-Type-Options" => "nosniff",
    "X-XSS-Protection" => "1; mode=block",
    "Referrer-Policy" => "strict-origin-when-cross-origin",
    "Content-Security-Policy" => "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'",
    "Permissions-Policy" => "geolocation=(), microphone=(), camera=()"
)

Hide server information

server.tag = ""

Disable server status module

server.modules -= ("mod_status")

Access restrictions

$HTTP["url"] =~ "^/(\.|_)" { url.access-deny = ("") }

Block common attack patterns

$HTTP["url"] =~ "(\.php$|\.asp$|\.aspx$)" { $HTTP["host"] !~ "^(api\.)" { url.access-deny = ("") } }

Rate limiting simulation with mod_rewrite

$HTTP["remoteip"] =~ ".*" { var.ratelimit = 1 }
sudo ln -s /etc/lighttpd/conf-available/20-security.conf /etc/lighttpd/conf-enabled/
sudo systemctl reload lighttpd

Common issues

SymptomCauseFix
Virtual host serves default pageHost header not matching exactlyCheck host configuration and DNS resolution with curl -H "Host: domain.com" http://server-ip/
SSL certificate not workingCertificate path incorrect or permissionsVerify paths in SSL config and check sudo certbot certificates
Permission denied errorsIncorrect file ownership or permissionsRun sudo chown -R www-data:www-data /var/www/ and sudo chmod -R 755 /var/www/*/public_html
Configuration test failsSyntax error in config filesRun sudo lighttpd -t -f /etc/lighttpd/lighttpd.conf to identify syntax errors
Log files not createdDirectory permissions or missing pathsEnsure log directories exist and are writable by www-data user
HTTPS redirect loopConflicting redirect rulesCheck for duplicate redirect configurations in virtual host files
Subdomain not resolvingDNS configuration missingAdd A records for subdomains pointing to server IP address

Next steps

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle managed cloud infrastructure for businesses that depend on uptime. From initial setup to ongoing operations.