Table of Contents

How to run a Caddy HTTP/2 TLS 1.3 Reverse Proxy with Let's Encrypt

Never again you will need to worry about HTTPS-enabling your NAS, your local webserver, your fileshare, your mediacenter and whatever else. Caddy will do that for you. Automatically. And Caddy will also make sure the certificates get re-issued automatically. All that you need is working DNS A records for your website(s).

Install Caddy

Execute as root. This should work on any debian-based distro:

apt install curl
curl https://getcaddy.com | bash -s personal http.nobots,http.minify,http.proxyprotocol
chown root:root /usr/local/bin/caddy
chmod 755 /usr/local/bin/caddy
setcap 'cap_net_bind_service=+eip' /usr/local/bin/caddy
mkdir -p /etc/caddy
chown -R root:www-data /etc/caddy
mkdir -p /etc/ssl/caddy
chown -R www-data:root /etc/ssl/caddy
chmod 770 /etc/ssl/caddy
touch /etc/caddy/Caddyfile
curl -L https://github.com/mholt/caddy/raw/master/dist/init/linux-systemd/caddy.service | sed "s/;CapabilityBoundingSet/CapabilityBoundingSet/" | sed "s/;AmbientCapabilities/AmbientCapabilities/" | sed "s/;NoNewPrivileges/NoNewPrivileges/" | tee /etc/systemd/system/caddy.service
chown root:root /etc/systemd/system/caddy.service
chmod 644 /etc/systemd/system/caddy.service
mkdir /var/log/caddy
chown -R www-data:root /var/log/caddy
chmod 770 /var/log/caddy
systemctl daemon-reload
systemctl enable caddy.service

Should you get an error 500 or similar while running the getcaddy.com script, grab your copy here.
https://gist.github.com/Jamesits/2a1e2677ddba31fae62d022ef8aa54dc
https://github.com/mholt/caddy/raw/master/dist/init/linux-systemd/caddy.service
https://caddyserver.com/download

Edit Caddyfile

Configure the Caddyfile located in /etc/caddy so that Caddy works as a reverse proxy. The first line is most important, it *MUST* be a working hostname and not an IP address for Let's Encrypt to generate certificates. Watch your syslogs. It doesn't matter if you are running an AWS or GCP instance behind a 1:1 NAT, the only thing that matters is that the box is available under the configured hostname and that port 80/443 is reachable. And make sure that the domain you use does not restrict letsencrypt.com by a CAA DNS record. If you want to serve more than one local webserver then just duplicate the following content and make sure the hostnames differ. Otherwise, if you want all your webservers to be reachable by the same hostname but under a different path (e.g. yourhost.example.com/mediacenter) then add more proxy{} sections. It's so simple.

yourhost.example.com {
log /var/log/caddy/yourhost.example.com.log
 
    proxy / 192.168.0.3:80 {
    transparent
    header_upstream Host {host}
    header_upstream X-Real-IP {remote}
    header_upstream X-Forwarded-For {remote}
    header_upstream X-Forwarded-Port {server_port}
    header_upstream X-Forwarded-Proto {scheme}
    }
}

Note: Certificates will re-generate when you change hostnames in Caddyfile followed by a restart. Now that's convenient.
The Caddyfile syntax

Start Caddy

That's it already. Start Caddy and give it a few seconds to grab the SSL certificates.

systemctl start caddy

Watch your syslogs to see the magic after you fire it up:

systemd[1]: Started Caddy HTTP/2 web server.
caddy[16065]: Activating privacy features... 
caddy[16065]: [INFO] [FileStorage:/etc/ssl/caddy] Started certificate maintenance routine
caddy[16065]: [INFO] [yourhost.example.com] acme: Obtaining bundled SAN certificate
caddy[16065]: [INFO] [yourhost.example.com] AuthURL: https://acme-v02.api.letsencrypt.org/acme/authz/blah
caddy[16065]: [INFO] [yourhost.example.com] acme: use tls-alpn-01 solver
caddy[16065]: [INFO] [yourhost.example.com] acme: Trying to solve TLS-ALPN-01
caddy[16065]: [INFO] [yourhost.example.com] The server validated our request
caddy[16065]: [INFO] [yourhost.example.com] acme: Validations succeeded; requesting certificates
caddy[16065]: [INFO] [yourhost.example.com] Server responded with a certificate.

Finito. There's your super small HTTPS reverse proxy serving at port 80 and port 443 and it will auto-redirect your visitors to HTTPS. Give it a try and enjoy your shiny new SSL certificates that work everywhere without warnings.

# ps aux | grep caddy
www-data 17694  0.0  2.2 116948 13088 ?        Ssl  00:38   0:00 /usr/local/bin/caddy <...>
 
# netstat -tulpn | grep caddy
tcp6       0      0 :::80                   :::*                    LISTEN      17694/caddy
tcp6       0      0 :::443                  :::*                    LISTEN      17694/caddy

Even the Qualys results are showing pretty lovely defaults: