Reverse Proxy with Caddy: HTTPS by Default
A reverse proxy is the entry point to all services — internal and external. Caddy is our choice because it automatically obtains and renews TLS certificates without anyone needing to think about it.
Why a Reverse Proxy?
All services behind one IP, one entry point, one SSL configuration. The reverse proxy terminates TLS and forwards traffic:
blog.senn-tech.com→ internal containersenn-next:3000helpdesk.senn-tech.com→ Zammad web interfacestatus.senn-tech.com→ Uptime-Kuma status page
This reduces the attack surface: only the proxy is exposed to the internet, while the services behind it communicate only on the internal network.
Caddy: The Caddyfile Is Enough
blog.senn-tech.com {
reverse_proxy senn-next:3000
}
helpdesk.senn-tech.com {
reverse_proxy zammad:8080
basicauth {
senn $2a$14$...
}
}
No Certbot cron, no manual certificate management. Caddy obtains and renews Let's Encrypt certificates automatically, transparently redirects HTTP to HTTPS, and speaks HTTP/3 (QUIC) without extra configuration. The first line alone is sufficient for TLS.
Upstreams with Their Own Certificate
If the backend service already speaks HTTPS — say, a gateway in front of an internal server — Caddy terminates TLS externally and forwards traffic internally over TLS. With self-signed internal certificates, we skip verification selectively:
intern.senn-tech.com {
reverse_proxy https://10.0.0.3:443 {
transport http {
tls_insecure_skip_verify
}
}
}
We use this exact pattern at our public gateway, which encrypts traffic and forwards it to the internal application server.
Patterns We Use
BasicAuth for Internal Services: Before Keycloak SSO takes effect, a simple BasicAuth adds an extra layer of protection. Two-factor in defense.
Security Headers: HSTS, X-Content-Type-Options, X-Frame-Options, and a Content Security Policy can be set centrally in a header block — defined once, imported everywhere.
Geo-Blocking: No access from countries where you don’t do business. Caddy can filter by region using @geoblock and the MaxMind module. This drastically reduces background noise in logs.
Rate Limiting and header hardening fit in just a few lines.
Caddy vs. Nginx vs. Traefik
Nginx can do everything, but its config syntax is its own dialect. Traefik shines in dynamic Docker environments: it auto-discovers containers via labels — ideal when services come and go constantly. Caddy sits in between: powerful enough for everything an SME needs, statically configured and thus easy to understand — the Caddyfile remains readable even after a year.
A Word on High Availability
A reverse proxy is a single point of failure. If Caddy goes down, everything is inaccessible from outside. We run Caddy under Proxmox as an HA-VM — if the host fails, the VM starts on the next node. The IP stays the same (thanks to an internal VLAN), and the services notice nothing.
Conclusion
A reverse proxy is not optional luxury; it’s the foundation of any multi-service infrastructure. Caddy makes it so simple that there’s no excuse not to use it.
Why Caddy and not Nginx or Traefik?+
Nginx can do everything, but its configuration syntax is its own dialect. Traefik shines in dynamic Docker environments where containers are auto-discovered via labels. Caddy sits in between: powerful enough for everything an SME needs, statically configured and easy to understand — the Caddyfile stays readable after a year. For stable, manageable multi-service infrastructures, it's the fairest choice.
How does a reverse proxy reduce the attack surface?+
Only the proxy is exposed to the internet; the services behind it communicate only on the internal network. Caddy terminates TLS, transparently redirects HTTP to HTTPS, and speaks HTTP/3 without extra configuration. Security headers like HSTS, X-Content-Type-Options, and a Content Security Policy can be set centrally, and geo-blocking filters access from countries you don't do business with.
What happens if Caddy fails?+
A reverse proxy is a single point of failure — if it goes down, everything is inaccessible from outside. That's why we run Caddy under Proxmox as an HA-VM: if the host fails, the VM starts on the next node, and the IP stays the same thanks to an internal VLAN. This secures the availability of the entry point without giving up the simplicity of a single Caddyfile.