Caddy: HTTPS reverse proxy in five lines
Caddy gets and renews TLS certificates on its own. Install it, write a Caddyfile with your domain and the port your app listens on, reload, and the site is live over HTTPS.
Drafted with AI from official documentation. Scripts were run in a test environment and repository links verified. Awaiting a human check. Did it work for you? Last edited Outdated?
Caddy is a web server whose main selling point is automatic HTTPS: give it a domain name and it obtains a certificate from Let’s Encrypt or ZeroSSL, redirects HTTP to HTTPS, renews before expiry and picks sane TLS settings, with nothing to configure. The configuration language, the Caddyfile, is designed to be short. This is the whole reverse proxy:
app.example.com {
reverse_proxy localhost:3000
}
Before you start
- A server with ports 80 and 443 open to the internet (
sudo ufw allow 80,443/tcpif you use ufw). - A DNS A record (and AAAA if applicable) for
app.example.compointing at that server. - Your application running and listening on a local port, here
3000.
1. Install Caddy
On Debian and Ubuntu, the official repository gives you a package plus a systemd service that runs Caddy as its own user:
Check before you run this
- Runs with administrator rights. Make sure you understand every line.
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
caddy version
Fedora and RHEL have a caddy package in the standard repositories (sudo dnf install caddy), and the install page covers Arch, Docker and static binaries. After installing, systemctl status caddy should show it running and serving a placeholder page on port 80.
2. Write the Caddyfile
The service reads /etc/caddy/Caddyfile. Replace its contents:
app.example.com {
reverse_proxy localhost:3000
}
That is the five lines, with whitespace. The first line is the site address. Because it is a real domain name, Caddy knows it must serve it over HTTPS and go and get a certificate. reverse_proxy forwards every request to the app and automatically sets X-Forwarded-For, X-Forwarded-Proto and X-Forwarded-Host; WebSockets work without extra configuration.
3. Reload
Check before you run this
- Runs with administrator rights. Make sure you understand every line.
caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
journalctl -u caddy -f
Within a few seconds the log shows the certificate being obtained and https://app.example.com works, including the redirect from http://. Careful: a reload applies the file to every site Caddy serves; the previous configuration is not kept, so keep a copy of the Caddyfile before editing (sudo cp /etc/caddy/Caddyfile /etc/caddy/Caddyfile.bak) and reload that copy to go back.
If the certificate step fails, the log says why. The usual causes are DNS not yet pointing at the server, port 80 or 443 blocked by a firewall or cloud security group, or another web server already using the ports (sudo ss -ltnp | grep -E ':(80|443)').
More than the minimum
{
email [email protected]
}
app.example.com {
encode zstd gzip
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options nosniff
Referrer-Policy strict-origin-when-cross-origin
-Server
}
handle_path /api/* {
reverse_proxy localhost:8000
}
handle {
reverse_proxy localhost:3000
}
log {
output file /var/log/caddy/app.log
}
}
www.example.com {
redir https://example.com{uri} permanent
}
example.com {
root * /var/www/example
file_server
}
- The
{ }block at the top is global options;emailis where Let’s Encrypt sends expiry warnings (which you should never receive, since Caddy renews on its own). encodecompresses responses.headeradds response headers; the-Serverline removes theServer: Caddyheader.handle_path /api/*routes requests under/api/to a second app and strips the prefix, so the backend sees/usersinstead of/api/users. Usehandleinstead ofhandle_pathto keep the prefix.- Several sites are just several blocks; each gets its own certificate.
caddy fmt --overwrite /etc/caddy/Caddyfiletidies the indentation.
Local and internal use
For a hostname that is not reachable from the internet, Caddy can issue certificates from its own local certificate authority instead:
intranet.lan {
tls internal
reverse_proxy localhost:3000
}
localhost {
reverse_proxy localhost:3000
}
localhost and names ending in .localhost get internal certificates automatically. caddy trust installs the local root into the system trust store so browsers accept them. To serve plain HTTP on purpose, write the address with a scheme or port: http://intranet.lan or :8080.
Where things are
- Configuration:
/etc/caddy/Caddyfile. Caddy also has a JSON config and a REST API onlocalhost:2019; the Caddyfile is converted to JSON internally. - Certificates and keys:
/var/lib/caddy/.local/share/caddy/. Back this folder up with the server; losing it only means certificates are requested again. - Logs:
journalctl -u caddy, or the file you configured withlog. - Run
caddy run --config /etc/caddy/Caddyfilein a terminal to see errors directly when debugging.
Official documentation
Did this work for you?
No votes yet. Be the first.
If it did not work, the report form below tells the editors what went wrong.
See also
- 0279 What is a reverse proxy? A reverse proxy is a server that sits in front of your apps, receives all requests and forwards them to the right… Beginner · Explains an idea · Any system Article Web servers & reverse proxies 15 Oct 2026
- 0408 What a reverse proxy is and when you need one A reverse proxy is a server that sits in front of your application, accepts requests from the internet and forwards them to… Beginner · Explains an idea · Any system Article Web servers & reverse proxies 15 Oct 2026
- 0409 Nginx as a reverse proxy with HTTPS Put Nginx in front of an app on localhost: a server block that proxies to the port, forwards the client headers, supports… Intermediate · Step by step · Linux Article Deploying sites · Web servers & reverse proxies 15 Oct 2026
- 0411 CORS explained, and how to fix the browser error The browser blocks a request from one origin to another unless the server allows it with Access-Control headers. Fix it in the… Intermediate · Step by step · Any system, Browser Article Making requests · Web servers & reverse proxies 15 Oct 2026
- 0412 Deploy a static site: from folder to a URL Take a folder of HTML, CSS and JavaScript and publish it at your own domain with HTTPS on a small Linux server… Beginner · Guided project · Linux, macOS Article Deploying sites · Web servers & reverse proxies 15 Oct 2026
- 0413 Fix: 502 Bad Gateway from Nginx Nginx is running but could not get a valid response from your application. Read the error log, check that the app is… Intermediate · Fixes an error · Linux Article Web servers & reverse proxies 15 Oct 2026
Repos in the wild
- Caddy: the web server with automatic HTTPS caddyserver/caddy Caddy is an open-source web server and reverse proxy written in Go that obtains and renews TLS certificates on its own, with a configuration file short enough to memorize and HTTP/1, 2 and 3 support out of the box. GitHub
- Traefik: a reverse proxy that configures itself from Docker and Kubernetes traefik/traefik Traefik is an open-source edge router that discovers services from Docker labels, Kubernetes resources and other providers and routes traffic to them, with automatic Let's Encrypt certificates and a dashboard. GitHub
History
Every saved edit is kept. The plot on the sleeve draws one line per edit. Open any edit to see exactly what changed.
- Dominik +5,159 chars