What is Caddy?
Caddy is a web server and reverse proxy written in Go. Its standout feature is automatic HTTPS. Specify a domain and Caddy automatically issues and renews certificates via the ACME protocol (Let's Encrypt / ZeroSSL), redirects HTTP to HTTPS, and even handles OCSP stapling for you. Unlike Nginx or Apache, you don't need a separate certificate tool (certbot) or to worry about cron-based renewals.
The Caddyfile configuration is declarative and concise. Nginx server blocks that run dozens of lines often collapse to just a few lines in Caddy. This guide covers installation, Caddyfile syntax, reverse proxying, TLS options, and the Admin API for zero-downtime reloads, all based on Caddy 2.x.
For certificate issuance, the domain must actually point at this server (A/AAAA records), and ports 80/443 must be reachable from the outside. The ACME HTTP-01 challenge arrives on port 80.
Installation
# Debian/Ubuntu save
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 caddy
# check
caddy versionInstalling via apt also creates a systemd service (caddy.service) and a dedicated caddy user. The config file lives at /etc/caddy/Caddyfile.
sudo systemctl enable --now caddy
systemctl status caddyCaddyfile Basics
The simplest static site:
example.com {
root * /var/www/example.com
file_server
encode gzip zstd
}Those three lines automatically issue a certificate for example.com, redirect HTTP to HTTPS, and serve static files. The site address on the first line of the block is the certificate issuance target.
You can group multiple domains in a single block.
example.com, www.example.com {
redir https://example.com{uri} permanent
}Placeholders such as {uri}, {host}, and {remote_host} are available (template variables). Wildcard certificates require a DNS-01 challenge, which means you need to build in a DNS provider plugin.
Reverse Proxy
This is one of Caddy's primary use cases. Put it in front of your backend application.
app.example.com {
reverse_proxy localhost:3000
}That's all you need for TLS termination plus proxying. Multiple upstreams get automatic load balancing.
api.example.com {
reverse_proxy {
to localhost:8001 localhost:8002 localhost:8003
lb_policy round_robin # least_conn, ip_hash
health_uri /healthz
health_interval 10s
health_timeout 3s
}
}To route different paths to different backends, use a matcher.
example.com {
# /api/* backend
reverse_proxy /api/* localhost:8080
# file
handle {
root * /var/www/spa
try_files {path} /index.html # SPA fallback
file_server
}
}Forwarding Headers to Upstream
By default, Caddy automatically attaches X-Forwarded-For, X-Forwarded-Proto, and X-Forwarded-Host. Manipulate additional headers like this:
reverse_proxy localhost:3000 {
header_up Host {upstream_hostport}
header_up X-Real-IP {remote_host}
header_down -Server # reply Server
}TLS Options
Leaving automatic HTTPS as-is is recommended, but you can tune it for operational needs.
example.com {
tls admin@example.com { # ACME
protocols tls1.2 tls1.3
}
reverse_proxy localhost:3000
}To use a private certificate on an internal network/for testing, or to disable issuance:
# certificate
tls /etc/ssl/site.crt /etc/ssl/site.key
# CA
tls internalWhile testing issuance limits, use Let's Encrypt staging instead of production. Set acme_ca https://acme-staging-v02.api.letsencrypt.org/directory in the global options block to avoid burning through rate limits.
Global Options and Logging
The curly-brace block at the very top of the file is global options.
{
email admin@example.com
admin localhost:2019 # Admin API
log {
output file /var/log/caddy/access.log
format json
}
}
example.com {
reverse_proxy localhost:3000
log {
output file /var/log/caddy/example.access.log {
roll_size 50mb
roll_keep 10
}
format console
}
}JSON logs can be parsed directly with jq, which is convenient for observability pipelines.
Config Validation and Zero-Downtime Reloads
#
caddy validate --config /etc/caddy/Caddyfile
caddy fmt --overwrite /etc/caddy/Caddyfile
# (graceful, )
sudo systemctl reload caddy
# or
caddy reload --config /etc/caddy/Caddyfilereload applies the new config without killing the process, so in-flight requests are not dropped. That's a major advantage in production.
Admin API
Caddy exposes a REST API on localhost:2019 by default. You can inspect and replace the config as JSON.
# settings check
curl -s localhost:2019/config/ | jq .
# check
curl -s localhost:2019/reverse_proxy/upstreams | jq .
# settings replace (PATCH)
curl -X POST localhost:2019/load \
-H "Content-Type: application/json" \
-d @new-config.jsonThe Admin API binds to localhost only by default, but it has no authentication. Never expose it to the outside. In container environments, be careful not to bind it to 0.0.0.0.
Troubleshooting
| Symptom | Cause / Check |
|---|---|
| Certificate issuance fails | Port 80 blocked by firewall, A record not set, rate limit |
ACME errors in journalctl -u caddy | DNS not fully propagated — check with dig example.com |
| 502 Bad Gateway | Upstream not running, typo in reverse_proxy port |
| Config not applied | Confirm you ran reload after caddy validate succeeded |
| TLS handshake error | Check protocols setting vs. client compatibility |
Always check logs in the systemd journal.
journalctl -u caddy -f --no-pagerSummary
| Item | Key point |
|---|---|
| Automatic HTTPS | Specify a domain and ACME issues and renews certificates automatically |
| Caddyfile | Declarative and concise; the site address is the certificate target |
| Reverse proxy | One-line reverse_proxy; LB and health checks with multiple upstreams |
| matcher | Branch by path/header; use try_files for SPAs |
| Zero-downtime reload | systemctl reload caddy / caddy reload |
| Admin API | localhost:2019; do not expose externally |
Caddy is a web server built on the philosophy that less configuration is better. Automatic HTTPS and a concise proxy syntax let you stand up an edge gateway for small-to-medium services quickly.
AI tools may assist drafting. Check the evidence in each guide for whether commands were run and which environment was used. Confirm the current docs for your version before applying instructions, and email us about errors. email us
Questions & answers
Ask a question about this guide. We'll review and reply.