Caddy: a one-config-file reverse proxy with automatic HTTPS
Caddy gives you HTTPS with no Certbot dance, multi-app reverse proxy in five lines of config, and HTTP/3 by default. Here is the setup we run.

If you've spent any time wrestling Nginx + Certbot, Caddy is going to feel like cheating. One binary, one config file, automatic HTTPS via Let's Encrypt with no extra tooling, HTTP/3 by default, sensible defaults. For most reverse-proxy use cases, it is now the right answer.
Install on Ubuntu 24.04
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
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
The package ships a systemd unit and starts the service. Caddy listens on 80 and 443 right away.
The Caddyfile
Caddy's config language is purpose-built and short. Edit /etc/caddy/Caddyfile:
example.com, www.example.com {
reverse_proxy localhost:3000
}
api.example.com {
reverse_proxy localhost:8080
log {
output file /var/log/caddy/api.log
}
}
files.example.com {
root * /var/www/files
file_server browse
encode gzip
}
Three sites, three different roles. No virtual host headers to debug, no SSL certificate paths to keep in sync, no Certbot timer.
sudo systemctl reload caddy
That's everything. DNS must point at the VPS, then Caddy provisions a Let's Encrypt cert on first request and renews automatically.
Common patterns
Single-page app with API on same domain
app.example.com {
handle /api/* {
reverse_proxy localhost:8080
}
handle {
root * /var/www/spa
try_files {path} /index.html
file_server
}
}
WebSocket-aware proxy
Caddy proxies WebSockets out of the box. No special config needed.
Basic auth on a subpath
admin.example.com {
basic_auth {
admin $2a$14$REPLACEMEHASH
}
reverse_proxy localhost:3001
}
Generate the hash with caddy hash-password.
IPv6-only backend
api.example.com {
reverse_proxy [::1]:8080
}
When not to use Caddy
- Custom TLS handshake gymnastics (mTLS to specific CAs, exotic cipher suites): Nginx is more configurable.
- Existing Nginx codebases: don't rewrite for the sake of it. Caddy is best when you start fresh.
- Edge-case load balancing with weighted upstreams, sticky sessions, etc: HAProxy still wins.
Logging and metrics
Caddy logs JSON by default. Pipe to journald or a file. For metrics:
{
servers {
metrics
}
}
Then curl http://localhost:2019/metrics for Prometheus-compatible output. Hook it into Grafana and you have a dashboard in ten minutes.
Auto-HTTPS exceptions
Caddy will try to obtain a cert for any hostname in your config. For internal-only or non-public sites, add auto_https off globally or use IP-bound site blocks.
That's the whole thing. Go read your last Nginx config and ask whether half of it is just TLS plumbing you no longer need.
Continue reading