Skip to content
Commitpedia
Log in Join & edit

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?

Edit history of Caddy: HTTPS reverse proxy in five lines: 1 recorded edit
Edit history · 1 edit
Type
Article
Edits
1
Editors
1
First issued
15 Oct 2026
Last edit
15 Oct 2026
Reading
3 min

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/tcp if you use ufw).
  • A DNS A record (and AAAA if applicable) for app.example.com pointing 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; email is where Let’s Encrypt sends expiry warnings (which you should never receive, since Caddy renews on its own).
  • encode compresses responses. header adds response headers; the -Server line removes the Server: Caddy header.
  • handle_path /api/* routes requests under /api/ to a second app and strips the prefix, so the backend sees /users instead of /api/users. Use handle instead of handle_path to keep the prefix.
  • Several sites are just several blocks; each gets its own certificate.
  • caddy fmt --overwrite /etc/caddy/Caddyfile tidies 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 on localhost: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 with log.
  • Run caddy run --config /etc/caddy/Caddyfile in 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

History

Every saved edit is kept. The plot on the sleeve draws one line per edit. Open any edit to see exactly what changed.

  1. Dominik +5,159 chars

Report a problem

Something wrong, unsafe or copied? Tell the editors.
What is wrong?