Reverse Proxies: Nginx, Caddy and What Sits in Front
Almost nothing should face the internet directly. A reverse proxy handles TLS, static files, buffering and routing so your application does not have to.
Application servers are built to run application code. They are generally poor at serving static files, terminating TLS, buffering slow clients and routing between services. A reverse proxy does those things well, which is why nearly every production stack has one.
What it takes off your hands
- TLS termination — certificates in one place, renewed automatically.
- Static file serving — far faster than any application runtime.
- Request buffering — a slow client occupies a cheap proxy connection instead of an expensive application worker.
- Compression — gzip or Brotli applied once, centrally.
- Routing — several applications or services behind one hostname and port.
- Rate limiting — abusive traffic stopped before it reaches your code.
- Caching — repeated responses served without touching the application.
- Header handling — security headers set consistently everywhere.
Request buffering is the underrated one. Without it, a client on a slow mobile connection ties up an application worker for the whole upload.
Nginx or Caddy
| Nginx | Caddy | |
|---|---|---|
| TLS certificates | Certbot or similar, configured | Automatic, by default |
| Configuration | Verbose, extremely well documented | Concise |
| Performance | Excellent | Excellent |
| Ecosystem | Vast; every answer already exists | Smaller but growing |
| Best for | Teams already familiar, complex routing | New setups, fast to a working state |
Caddy gets a site to working HTTPS in about five lines and handles renewal without a cron job. Nginx has an answer to every unusual requirement and a decade of documented solutions. Neither is a bad choice; pick for familiarity.
Traefik is a third option worth knowing if you run containers, since it configures routes from container labels automatically.
The details that go wrong
- Forward the client address. Set
X-Forwarded-ForandX-Forwarded-Proto, and configure the application to trust the proxy. Otherwise every request appears to come from 127.0.0.1. - Set body size limits. Nginx defaults to 1 MB, which breaks file uploads with a confusing 413.
- Set timeouts deliberately. Proxy read timeouts shorter than your slowest endpoint produce 504s that look like application errors.
- Enable HTTP/2, which is one directive and a real latency improvement.
- Serve static files directly rather than proxying them to the application.
- Add security headers once, at the proxy: HSTS, X-Content-Type-Options, Referrer-Policy, and a content security policy if you can maintain one.
Bind the application to localhost
With a proxy in front, the application should listen on 127.0.0.1 or a Unix socket, never on 0.0.0.0. This means a firewall mistake cannot expose it directly, and all traffic necessarily passes through the proxy’s rules.
It is one configuration line and it removes an entire class of accidental exposure.
Caching at the proxy
Nginx can cache upstream responses, which turns repeated dynamic requests into static ones. Even a ten-second cache on a busy page removes most of the load.
- Cache only responses that are safe to share between users. Never cache a page containing session-specific content.
- Vary on the headers that genuinely change the response, and no more.
- Ignore query parameters you do not use, so cache busting fails.
- Set
stale-while-revalidatebehaviour so users get a fast response while the cache refreshes.
Proxy or managed load balancer
They overlap. A managed load balancer gives redundancy and managed certificates without maintenance; a self-hosted proxy gives caching, rewriting and routing logic that managed products often lack.
A common arrangement uses both: a managed load balancer for TLS and distribution, and a proxy on each server for caching, static files and application-specific routing.
Common questions
Do I need a reverse proxy for a single application?
Almost always yes. TLS handling, static file performance and request buffering are worth it even for one small service.
Is Apache a reasonable choice?
Yes, particularly where .htaccess support matters or existing configuration assumes it. Nginx and Caddy use less memory under high concurrency, which is why new deployments usually pick one of them.
How do I get HTTPS certificates?
Caddy does it automatically. With Nginx, use Certbot with a renewal timer. Managed load balancers issue and renew certificates for you.
Should the proxy talk HTTPS to the application?
Within a single host or trusted private network, plain HTTP is normal and simpler. Across a network boundary, or for regulated data, encrypt that hop too.
Where to go next
See load balancing basics and CDN and caching.