Networking
What is a reverse proxy? Nginx, Caddy and Traefik compared
A reverse proxy sits in front of your apps, handles HTTPS and routing, and hides the servers behind it. How it works, why you want one, and how Nginx, Caddy and Traefik differ.
By Raktim Ranjit · Published · 3 min read
Short answer: a reverse proxy is a server that receives requests from the internet and forwards them to the right application behind it, then returns the response. It lets many apps share ports 80 and 443, terminates HTTPS in one place, and keeps your applications off the public network. Caddy is the simplest, Nginx the most widely used, and Traefik is built for containers.
How is it different from a forward proxy?
A forward proxy sits in front of clients and fetches things for them, like a corporate proxy or a VPN. A reverse proxy sits in front of servers. The visitor talks to the proxy and never knows which machine or port actually produced the page.
What does it do for you?
- TLS termination: one place for certificates. Apps behind it can speak plain HTTP on a private network.
- Routing by hostname or path:
blog.example.comgoes to one app,app.example.comto another,/apito a third. - One public entry point: only ports 80 and 443 are open, not a dozen app ports.
- Load balancing: spread requests over several copies of an app.
- Compression and caching of static content.
- Security controls: rate limits, headers, access rules and request size limits, covered in reverse proxy hardening.
- Zero-downtime changes: switch the upstream without touching DNS.
How does it look in each tool?
Caddy
app.example.com {
reverse_proxy 127.0.0.1:3000
}
blog.example.com {
reverse_proxy 127.0.0.1:8080
}HTTPS certificates are obtained and renewed automatically. Very little configuration.
Nginx
server {
listen 443 ssl;
server_name app.example.com;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1: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;
}
}Very fast, very configurable, huge amount of documentation. Certificates need Certbot or similar, and every proxy header is spelled out.
Traefik
Traefik reads Docker labels, so a new container can register its own route without editing a central config.
services:
whoami:
image: traefik/whoami
labels:
- traefik.http.routers.whoami.rule=Host(`who.example.com`)
- traefik.http.routers.whoami.tls.certresolver=leIt suits setups where services come and go. The label syntax takes time to learn, and giving it access to the Docker socket is a security trade-off to handle carefully.
Which should you pick?
- One or a few self-hosted apps and you want it working today: Caddy.
- You need advanced behaviour, already know Nginx, or run high traffic: Nginx.
- Many containers that change often: Traefik, or Caddy with a Docker plugin.
What mistakes are common?
- Forgetting to pass the original host and protocol headers, so the app generates
http://links or redirect loops. - WebSocket apps that break because upgrade headers are not forwarded. Nginx needs
proxy_set_header UpgradeandConnection. - Leaving the app's own port open on the public interface, which bypasses the proxy completely.
- Trusting
X-Forwarded-Forfrom anyone instead of only from the proxy. - Not persisting Caddy's data directory, so certificates are re-requested.
If you cannot open ports at home at all, look at Cloudflare Tunnel versus port forwarding. It still uses the idea of a front door, just reached differently.
References
Author
Raktim Ranjit is a software engineer and the founder of NodeDR Infotech. He builds and maintains the software described here.