Caddy is a modern web server written in Go whose headline feature is automatic HTTPS: point a domain at the server, name it in the config, and Caddy obtains and renews certificates by itself. It also enables HTTP/2 and HTTP/3 by default and has a compact, readable config format.
1. Install from the official 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
caddy version
(Check caddyserver.com for current instructions; RHEL-family systems use a COPR repository.)
The package creates a caddy system user and a systemd service, and serves a welcome page on port 80.
2. Open the firewall
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 443/udp # HTTP/3
Port 80 must be reachable for certificate issuance (HTTP challenge) and redirects.
3. Serve a static site
Put files in place:
sudo mkdir -p /var/www/example.com
echo '<h1>Hello from Caddy</h1>' | sudo tee /var/www/example.com/index.html
Edit /etc/caddy/Caddyfile:
example.com, www.example.com {
root * /var/www/example.com
file_server
encode zstd gzip
}
Reload:
sudo systemctl reload caddy
That’s it. With DNS pointing to the server, Caddy obtains certificates for both names within seconds, redirects HTTP to HTTPS, and serves the site. Check progress with journalctl -u caddy -f.
Redirect www to the bare domain instead:
www.example.com {
redir https://example.com{uri} permanent
}
example.com {
root * /var/www/example.com
file_server
}
4. Reverse proxy an app
app.example.com {
reverse_proxy 127.0.0.1:3000
}
Headers (X-Forwarded-For, X-Forwarded-Proto), WebSockets and HTTP/2 are handled automatically. More in Caddy as a reverse proxy.
5. PHP with PHP-FPM
sudo apt install -y php-fpm
blog.example.com {
root * /var/www/blog
php_fastcgi unix//run/php/php8.3-fpm.sock
file_server
}
php_fastcgi includes the usual front-controller rewrites, so WordPress and Laravel-style pretty URLs work without extra rules. PHP-FPM’s socket must be accessible to the caddy user — add it to the www-data group (sudo usermod -aG www-data caddy) or adjust the pool’s listen.owner/listen.group, then restart both services.
6. Global options
At the top of the Caddyfile:
{
email [email protected]
# admin off
}
The email is used for the ACME account (expiry notices). Caddy exposes an admin API on localhost:2019 by default, which caddy reload uses; you can disable it if you don’t need it.
7. Managing Caddy
sudo systemctl reload caddy # graceful config reload
caddy validate --config /etc/caddy/Caddyfile
caddy fmt --overwrite /etc/caddy/Caddyfile
journalctl -u caddy --since today
Certificates and keys live in /var/lib/caddy/.local/share/caddy/ — include it in backups to avoid re-issuing everything after a rebuild.
8. Logs
example.com {
log {
output file /var/log/caddy/example.com.log
format json
}
root * /var/www/example.com
file_server
}
Create /var/log/caddy owned by caddy. Caddy rotates its log files itself.
Caddy vs Nginx
| Caddy | Nginx | |
|---|---|---|
| HTTPS | Automatic | Certbot or manual |
| Config | Short Caddyfile | More verbose, very flexible |
| HTTP/3 | On by default | Supported, needs configuration |
| Ecosystem/tutorials | Growing | Enormous |
| Raw performance | Excellent | Excellent |
For most sites either is fine. Caddy wins on simplicity; Nginx on ubiquity and advanced tuning — see installing Nginx. Want Caddy in containers? See Caddy with Docker Compose.