Nginx vs Caddy — Which Reverse Proxy Should You Use?
I used Nginx for years before switching to Caddy. I wrote about the migration in a previous post, but I didn’t do a proper side-by-side comparison. If you’re choosing between the two right now, here’s what actually matters.
Configuration
This is where the difference hits you immediately.
An Nginx config for a reverse proxy with SSL:
server {
listen 80;
server_name app.example.com;
return 301 https://$server_name$request_uri;
}
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://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;
}
}
The same thing in Caddy:
app.example.com {
reverse_proxy localhost:3000
}
Three lines. HTTPS is automatic. HTTP-to-HTTPS redirect is automatic. Proxy headers are set by default. The Caddyfile format is not JSON, not YAML, not XML — it’s its own thing, and it’s remarkably readable.
Nginx’s config language is powerful but verbose. For simple reverse proxy setups, most of that verbosity is boilerplate you copy-paste between sites. Caddy eliminates that boilerplate entirely.
For complex routing — regex-based rewrites, conditional logic, fine-grained location matching — Nginx’s config language is more expressive. Caddy can handle most of these cases with matchers and handle blocks, but Nginx has the edge when things get complicated.
Automatic HTTPS
This is Caddy’s headline feature, and it genuinely changes how you work.
With Nginx, HTTPS means installing Certbot, running it for each domain, setting up a cron job for renewals, and debugging when renewals fail. It works, but it’s manual overhead that scales linearly with the number of domains you manage.
Caddy provisions Let’s Encrypt certificates automatically the moment you add a domain to the config. Renewals happen in the background. If a renewal fails, Caddy retries and logs the error. I haven’t thought about SSL certificates since switching.
Caddy also supports ACME DNS challenges out of the box, which means you can get certificates for internal services or wildcard domains without exposing port 80 to the internet. With Nginx, this requires additional tooling.
Performance
Let’s be honest: for the vast majority of applications, both are fast enough that you’ll never notice the difference.
Nginx is written in C and has been optimized for decades. It’s the go-to for high-traffic sites — think millions of requests per second. Nginx can handle more concurrent connections with less memory, and its event-driven architecture is battle-tested at scale.
Caddy is written in Go. It’s fast, but it won’t match Nginx in raw throughput benchmarks. The practical difference shows up at extreme scale — if you’re serving a massive amount of static files or proxying thousands of concurrent WebSocket connections, Nginx will perform better.
For most web applications — an API serving a few hundred or even a few thousand requests per second — Caddy handles it without breaking a sweat. I’ve never had a performance issue with Caddy on any client project.
Ecosystem and Community
Nginx has been around since 2004. The ecosystem is massive. Every hosting tutorial, every Docker guide, every production setup guide defaults to Nginx. If you hit a problem, someone has solved it before.
Caddy is newer (2015) and the community is smaller. Documentation is good, but you’ll find fewer Stack Overflow answers and blog posts. The plugin ecosystem is growing but can’t match Nginx’s module library.
If you need something like GeoIP-based routing, advanced rate limiting, or Lua scripting, Nginx has mature solutions. Caddy might have a community plugin for it, or you might need to write your own.
Reload and Config Changes
Nginx requires you to test the config (nginx -t) and reload (systemctl reload nginx) every time you make a change. If the config has a syntax error, the reload fails and nothing changes — which is actually a nice safety net.
Caddy can reload its config with zero downtime via an API call or by sending a signal. It also validates the config before applying it. In Docker, I just update the Caddyfile and restart the container — Caddy picks up the new config immediately.
When to Use Nginx
- You’re running a high-traffic application where every millisecond of latency matters
- You need advanced features like Lua scripting, complex rewrite rules, or specific modules
- You’re working in an environment where Nginx is already established and the team knows it
- You need fine-grained control over caching, connection pooling, or upstream health checks
When to Use Caddy
- You want automatic HTTPS without managing Certbot
- You’re deploying multiple services on subdomains and want minimal config
- You’re running on a VPS with Docker and want something simple to maintain
- You don’t want to deal with SSL certificate management ever again
- You’re a solo developer or small team that values simplicity
What I Use
I use Caddy for all my projects now. The automatic HTTPS alone saved me enough headaches to justify the switch. The simpler config means I spend less time on infrastructure and more time building actual features.
If I were working at a company serving millions of requests per second, I’d use Nginx (or more likely, the team would already be using it). But for client projects, personal projects, and any VPS-based deployment, Caddy is the better choice for my workflow.
The best reverse proxy is the one that stays out of your way. For me, that’s Caddy.
Related
Git Tags, Drone CI, and Watchtower — A Simple Deployment Pipeline
How I moved from Jenkins to Drone CI with git tags and Watchtower for a lightweight, reliable deployment pipeline.
Justfile vs Makefile — Which Task Runner Should You Use?
Comparing just and make as task runners for modern development workflows — syntax, features, and when each makes sense.
Migrating from Nginx to Caddy — Why I Switched and Never Looked Back
How Caddy replaced Nginx as my reverse proxy — automatic HTTPS, wildcard domains, and a config file that actually makes sense.