Caddy vs nginx vs Traefik — choosing the right reverse proxy
Three reverse proxies cover the vast majority of practical deployments in 2026. Each is best at something specific. This article picks them apart so you can choose deliberately rather than by inertia.
The short version
- Caddy — pick this when you want automatic TLS and a config file you can actually read. Right answer for most personal and small-business deployments.
- nginx — pick this when you need fine- grained control, you're scaling beyond what Caddy's config DSL handles cleanly, or you're working with existing infrastructure that uses it.
- Traefik — pick this when you're running Docker / Kubernetes and want the proxy to auto-discover services from labels.
Caddy
The newest of the three, designed around automatic TLS via Let's Encrypt out of the box.
Minimal config (/etc/caddy/Caddyfile):
example.com {
reverse_proxy localhost:3000
}
api.example.com {
reverse_proxy localhost:8000
}
That's it. Caddy obtains certs, sets up HTTP→HTTPS redirects, configures HSTS, and proxies traffic. No 200-line nginx config to translate.
Strengths:
- Automatic TLS with zero config.
- HTTP/3 (QUIC) by default.
- Config DSL is dramatically more readable than nginx's.
- Sane defaults for security headers.
Weaknesses:
- Less third-party tooling and community documentation than nginx.
- Performance is excellent but slightly behind nginx at extreme scale (most users will never notice).
- Plugin ecosystem smaller than nginx modules.
nginx
The veteran. Battle-tested, omnipresent, documented exhaustively.
Equivalent config:
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Strengths:
- Maximally documented — Stack Overflow has a snippet for anything you'd want to do.
- Maximum control over caching, rate limiting, complex routing.
- Fastest at extreme scale (the difference is small but real).
- Lua scripting (with openresty) opens up custom logic.
Weaknesses:
- Manual TLS — need certbot or similar.
- Config syntax is finicky; one missing semicolon breaks everything.
- Default security headers are absent — you have to set them.
Traefik
Designed for dynamic environments — Docker, Kubernetes, service discovery. Reads labels off your containers and configures routes automatically.
In a Docker Compose context:
services:
myapp:
image: myapp:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.myapp.rule=Host(`app.example.com`)"
- "traefik.http.routers.myapp.tls.certresolver=lets-encrypt"
- "traefik.http.services.myapp.loadbalancer.server.port=3000"
Traefik picks up the labels, obtains TLS certs, and routes requests. Add a new service with appropriate labels and it works without restarting Traefik.
Strengths:
- Auto-discovery for containerised workloads.
- Built-in dashboard.
- First-class Kubernetes Ingress support.
- Automatic TLS like Caddy.
Weaknesses:
- Steeper learning curve for the static config side.
- Overkill for a single-VPS, few-app deployment.
- Documentation is good but the abstraction model takes getting used to.
The decision matrix
| Situation | Pick |
|---|---|
| Single VPS, a handful of services, you want quick HTTPS | Caddy |
| You're inheriting an nginx config and don't want to migrate | nginx |
| You need complex routing rules (path rewrites, A/B testing, header munging) | nginx |
| You're running Docker / k8s with many services | Traefik |
| You need rate limiting and request caching | nginx |
| You want one-line config per site | Caddy |
| You need HTTP/3 today | Caddy |
| You're behind Cloudflare and just need an origin proxy | Any (nginx is most common, Caddy is simplest) |
Migration paths
If you're considering switching:
- nginx → Caddy: usually a win for configuration sanity. Test thoroughly because Caddy's defaults are different (HSTS on by default, etc.).
- Caddy → nginx: do this only when you've hit a specific feature Caddy can't do. Don't switch because nginx is "more enterprise."
- nginx → Traefik: only when you're also moving to containerized deployment. Otherwise you're adding complexity without benefit.
Honourable mentions
- HAProxy: excellent for TCP / Layer 4 load balancing. Less convenient for HTTPS termination than the three above. Pick if you specifically need L4.
- Apache: still works fine; just rarely the best choice for new deployments. If your team knows Apache, fine.
- Envoy: incredibly capable but complexity scales with that capability. Wait until you're doing service-mesh things before reaching for it.
Also Read
Powered by WHMCompleteSolution