Best Lightweight Web Server 2024: Nginx, Caddy, or Lighttpd?

by David Park
Best Lightweight Web Server 2024: Nginx, Caddy, or Lighttpd?

Picking the best lightweight web server in 2024 means choosing between three battle-tested options — Nginx, Caddy, and Lighttpd — each with a different trade-off between memory footprint, configuration complexity, and built-in features. By the end of this guide you'll have all three installed on Ubuntu 24.04, a side-by-side benchmark, and a concrete recommendation for static sites, reverse proxies, and low-RAM VPS setups.

Prerequisites

  • Ubuntu 24.04 LTS (fresh install or existing server)
  • A non-root user with sudo privileges
  • At least 512 MB RAM (1 GB recommended for running all three side-by-side)
  • Basic familiarity with systemd and editing files with nano or vim
  • Ports 80 and 443 open in your firewall

What "Lightweight" Actually Means in 2024

In enterprise circles, "lightweight" is marketing. For indie hackers running a $4 Hetzner CAX11, it means three concrete things:

  1. Idle RSS memory — how much RAM the process holds when no requests are in flight.
  2. Worker model — event-driven servers handle more concurrent connections per MB than prefork servers.
  3. Cold-start time — relevant for containers and autoscaling; you want sub-second restarts.

The three contenders here are:

Server Idle RSS Worker model HTTPS auto-provision
Nginx 1.26 ~4 MB Event-driven No (manual or with Certbot)
Caddy 2.8 ~25 MB Event-driven Yes (built-in ACME)
Lighttpd 1.4 ~2 MB Event-driven No (manual)

Numbers are from a single-worker process on Ubuntu 24.04 with no active connections.


Step 1 — Install All Three Servers

Install each server from the official Ubuntu 24.04 repositories or upstream binaries. Do not run them simultaneously on the same port; stop each before starting the next.

1.1 Install Nginx

sudo apt update && sudo apt install -y nginx

Expected output ends with:

Setting up nginx (1.26.x-...) ...

1.2 Install Lighttpd

sudo apt install -y lighttpd

1.3 Install Caddy

Caddy is not in the default Ubuntu repos. Add the official Caddy repository:

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
  | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
  | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update && sudo apt install -y caddy

Verify the installed version:

caddy version
v2.8.4 h1:...

Step 2 — Benchmark Idle Memory Footprint

Start each server one at a time and record its RSS. Stop the previous one before starting the next to avoid port conflicts.

2.1 Measure Nginx

sudo systemctl start nginx
ps aux --sort=rss | grep nginx | grep -v grep
www-data  1234  0.0  0.3   5120  4096 ?  S  10:00  0:00 nginx: worker process
root      1233  0.0  0.1   4096  2048 ?  Ss 10:00  0:00 nginx: master process
sudo systemctl stop nginx

2.2 Measure Lighttpd

sudo systemctl start lighttpd
ps aux --sort=rss | grep lighttpd | grep -v grep
www-data  1301  0.0  0.2   3072  2048 ?  S  10:01  0:00 /usr/sbin/lighttpd -D -f /etc/lighttpd/lighttpd.conf
sudo systemctl stop lighttpd

2.3 Measure Caddy

sudo systemctl start caddy
ps aux --sort=rss | grep caddy | grep -v grep
caddy     1402  0.1  1.8  26624 25600 ?  Ssl 10:02  0:00 /usr/bin/caddy run --environ --config /etc/caddy/Caddyfile
sudo systemctl stop caddy

Caddy's higher idle RSS comes from the Go runtime and the built-in ACME client. On a 1 GB VPS that's a non-issue. On a 256 MB container it matters.


Step 3 — Configure Each Server for a Static Site

All three will serve /var/www/html on port 80. Create a test page first:

echo '<h1>Hello from serverhabit.com</h1>' | sudo tee /var/www/html/index.html

3.1 Nginx — /etc/nginx/sites-available/default

Edit the default server block:

sudo nano /etc/nginx/sites-available/default

Replace the contents with:

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    root /var/www/html;
    index index.html;

    server_name _;

    location / {
        try_files $uri $uri/ =404;
    }

    # Tune for low memory
    sendfile on;
    tcp_nopush on;
    keepalive_timeout 15;
}

Test and reload:

sudo nginx -t && sudo systemctl reload nginx

3.2 Lighttpd — /etc/lighttpd/lighttpd.conf

The default config already serves /var/www/html. Verify the document root setting:

grep 'server.document-root' /etc/lighttpd/lighttpd.conf
server.document-root        = "/var/www/html"

Start Lighttpd:

sudo systemctl start lighttpd

3.3 Caddy — /etc/caddy/Caddyfile

Edit the Caddyfile:

sudo nano /etc/caddy/Caddyfile

Replace the contents with:

:80 {
    root * /var/www/html
    file_server
    encode gzip
}

Reload Caddy:

sudo systemctl reload caddy

Step 4 — Load Test with wrk

Install wrk and run a 30-second, 100-connection benchmark against each server. Run one server at a time.

sudo apt install -y wrk

Test Nginx (start it first):

sudo systemctl start nginx
wrk -t4 -c100 -d30s http://localhost/
sudo systemctl stop nginx
Running 30s test @ http://localhost/
  4 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.02ms    0.45ms  12.34ms   89.23%
    Req/Sec    24.87k     1.23k   28.10k    72.00%
  2,981,540 requests in 30.05s, 723.45MB read
Requests/sec:  99,217.33
Transfer/sec:     24.08MB

Test Lighttpd:

sudo systemctl start lighttpd
wrk -t4 -c100 -d30s http://localhost/
sudo systemctl stop lighttpd
  2,743,210 requests in 30.05s, 665.10MB read
Requests/sec:  91,289.45
Transfer/sec:     22.13MB

Test Caddy:

sudo systemctl start caddy
wrk -t4 -c100 -d30s http://localhost/
sudo systemctl stop caddy
  2,601,880 requests in 30.05s, 631.00MB read
Requests/sec:  86,581.22
Transfer/sec:     20.99MB

All three clear 85k req/sec on a single-core VPS for static files. The performance gap between them is smaller than the gap between your server and your CDN edge node.


Step 5 — Reverse Proxy Configuration

For a Node.js or Python app running on 127.0.0.1:3000, here is the minimal reverse proxy config for each.

5.1 Nginx reverse proxy block

Add inside the server {} block in /etc/nginx/sites-available/default:

location /app/ {
    proxy_pass http://127.0.0.1:3000/;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
}

5.2 Lighttpd reverse proxy

Enable the proxy module, then add to /etc/lighttpd/lighttpd.conf:

sudo lighty-enable-mod proxy
proxy.server = (
    "/app" => (
        "node" => (
            "host" => "127.0.0.1",
            "port" => 3000
        )
    )
)

Restart Lighttpd after editing:

sudo systemctl restart lighttpd

5.3 Caddy reverse proxy

In /etc/caddy/Caddyfile, add:

:80 {
    handle /app/* {
        reverse_proxy 127.0.0.1:3000
    }
    handle {
        root * /var/www/html
        file_server
    }
    encode gzip
}

Caddy's reverse proxy automatically strips and rewrites headers, handles WebSockets, and retries on upstream failure — no extra directives needed.


Verify It Works

Run these checks after starting whichever server you choose.

Check the HTTP response:

curl -I http://localhost/
HTTP/1.1 200 OK
Server: nginx/1.26.x   # or lighttpd/1.4.x or Caddy
Content-Type: text/html

Check the process is running and its port is bound:

sudo ss -tlnp | grep ':80'
LISTEN  0  511  0.0.0.0:80  0.0.0.0:*  users:(("nginx",pid=1234,fd=6))

Check systemd reports the service active:

sudo systemctl status nginx   # or caddy / lighttpd

Look for Active: active (running).

Tail the access log to confirm requests land:

# Nginx
sudo tail -f /var/log/nginx/access.log

# Lighttpd
sudo tail -f /var/log/lighttpd/access.log

# Caddy
sudo journalctl -u caddy -f

Troubleshooting

Port 80 already in use — bind() to 0.0.0.0:80 failed Run sudo ss -tlnp | grep ':80' to find the conflicting process, then stop it with sudo systemctl stop <service>.

Nginx config test fails — nginx: [emerg] unknown directive Run sudo nginx -t to get the exact line number. Common cause: a missing semicolon at the end of a directive.

Caddy fails to start — permission denied binding to port 80 Caddy's systemd unit already has AmbientCapabilities=CAP_NET_BIND_SERVICE. If you installed Caddy manually (not via apt), grant the capability: sudo setcap cap_net_bind_service=+ep /usr/bin/caddy.

Lighttpd returns 403 on / The document root directory permissions are too restrictive. Run sudo chmod 755 /var/www/html and sudo chown -R www-data:www-data /var/www/html.

Caddy ACME certificate fails Ensure your domain's A record points to the server's public IP and port 443 is open. Check logs with sudo journalctl -u caddy --since '5 min ago'.

High memory after load test Nginx and Lighttpd don't release worker memory aggressively. Restart the service to reclaim it: sudo systemctl restart nginx. For production, tune worker_processes auto; and worker_connections 1024; in /etc/nginx/nginx.conf.


Which Is the Best Lightweight Web Server in 2024?

Here is the honest breakdown:

  • Choose Nginx if you need a battle-hardened reverse proxy with the largest ecosystem of modules, tutorials, and StackOverflow answers. It is the safest default for most setups.
  • Choose Caddy if you want automatic HTTPS with zero Certbot configuration and you can spare the extra 20 MB of RAM. It wins on operator time saved.
  • Choose Lighttpd if you are running on a 256 MB or 512 MB container where every megabyte counts, or serving a high-volume static asset CDN origin where raw throughput per MB of RAM is the metric.

For a typical indie hacker stack — one or two apps behind a reverse proxy, a $6–$20/month VPS, HTTPS required — Caddy is the best lightweight web server in 2024 for operator simplicity, and Nginx is the best for ecosystem depth. Lighttpd remains the right call only when RAM is the hard constraint.

Next steps: pair whichever server you choose with a systemd hardening unit (ProtectSystem=strict, NoNewPrivileges=true) and a fail2ban jail on the access log to block brute-force scanners.