Topic cluster
Reverse proxy configuration
nginx, HAProxy, Envoy, Caddy and Traefik: routing semantics, upstream selection, health checks, WebSockets, gRPC and session affinity, compared honestly.
A reverse proxy is the piece of infrastructure with the highest ratio of "looks simple" to "has surprising semantics". Two lines of configuration decide whether a path prefix is stripped, whether a WebSocket survives, whether a failed backend is retried, and whether the application ever learns who the client was.
This cluster covers the five reverse proxies most teams actually run, with an emphasis on the behaviour that differs between them rather than a restatement of each manual.
Choosing#
nginx vs HAProxy vs Envoy vs Caddy vs Traefik is the decision page. The short form:
- nginx if you also serve static content and want a configuration language your whole team already reads.
- HAProxy if load balancing correctness, L4 work and runtime control without reloads matter most.
- Envoy if you need dynamic configuration, first class gRPC and deep observability, and can afford the operational surface.
- Caddy if you want automatic HTTPS and the smallest possible operational footprint.
- Traefik if your routing should be derived from container or Kubernetes metadata rather than written by hand.
Per proxy#
nginx as a reverse proxy is the longest of these, because nginx has the most defaults that surprise people and the most configuration that has to be written by hand. The others follow the same shape: HAProxy, Envoy, Caddy and Traefik.
The behaviours that catch everyone#
- Path prefix handling. nginx proxy_pass and the trailing slash is the canonical example, and the proxy_pass simulator will tell you what your own configuration does. Once the prefix is stripped, running an app under a subpath covers how the application gets it back:
X-Forwarded-Prefixper framework, or a base path, plus the redirects, cookies and links the proxy still has to fix. When a redirect comes back with:8080in it, or withhttp://behind TLS, why nginx redirects carry the wrong port or scheme separates the redirects nginx builds from the ones the backend sends and shows which ofabsolute_redirect,port_in_redirectandproxy_redirectfixes each. - TLS to the backend. When Apache sits behind the proxy, why Apache returns 421 Misdirected Request after 2.4.64 explains the SNI check that started refusing proxies which send no server name, the nginx, HAProxy and Caddy lines that fix it, and the connection reuse that breaks the fix again.
- Protocol upgrades. WebSockets and gRPC both need explicit configuration in some proxies and none at all in others.
- Failure handling. Health checks and upstream failover covers the gap between "the backend is down" and "the proxy noticed". When there is nothing left to fail over to, showing a custom error or maintenance page when the backend is down explains why nginx needs only
error_page 502 504and notproxy_intercept_errors, why HAProxy's dead-server status is 503, and which mechanism catches a backend's own 500 in HAProxy, Caddy and Traefik. - State. Sticky sessions explains when affinity is a legitimate optimisation and when it is a bug you have decided to keep.
- Migration. ingress-nginx is retired, and what replaces proxy-body-size, proxy-read-timeout and the other proxy annotations maps each one to ingress2gateway's output, Gateway API, Envoy Gateway and Traefik, including the routes that drop from a 60 second timeout to 15.
- Authentication. nginx auth_request vs Traefik ForwardAuth vs Caddy forward_auth explains why the login redirect from Authelia or authentik works in two of them and returns a 500 in nginx.
Every guide in this cluster#
- Apache 421 Misdirected Request after 2.4.64: the fixApache 2.4.64 returns 421 when a proxy sends no SNI for a non-default vhost. Tested fixes for nginx, HAProxy and Caddy, and what SSLVHostSNIPolicy changes.
- App under /app/ behind a proxy: X-Forwarded-PrefixStrip /app/ at the proxy and send X-Forwarded-Prefix, or keep it and set a base path. Which frameworks read the header, and the redirects and cookies they miss.
- Caddy reverse proxyCaddy's reverse_proxy directive, automatic HTTPS, load balancing and health checks, the handle vs handle_path trap, and Caddy versus nginx defaults.
- Custom error page when the backend is down: nginx and moreerror_page 502 504 catches a dead backend without proxy_intercept_errors. Tested rules for nginx, HAProxy errorfile, Caddy handle_errors and Traefik errors.
- Envoy listeners, routes and clustersHow Envoy's listener, filter chain, route table and cluster model fits together, what xDS does, the full timeout ladder, and the response flag table for debugging.
- nginx auth_request vs ForwardAuth: why the 302 failsWhere nginx auth_request, Traefik ForwardAuth and Caddy forward_auth differ with Authelia, authentik or oauth2-proxy: redirects, headers, bodies and spoofing.
- gRPC through a reverse proxyWhy gRPC needs end-to-end HTTP/2, how trailers and buffering break naive proxies, config for nginx, Envoy, HAProxy and Traefik, and why L4 balancing pins RPCs
- HAProxy configuration for HTTP reverse proxyingHow HAProxy sections, rule ordering, timeouts and the runtime API fit together, with a production-shaped config and a timeout-to-log-flag lookup table.
- Health checks and upstream failoverActive vs passive checks across nginx, HAProxy, Envoy, Traefik and Caddy, why deep health endpoints cause total outages, and the correct draining order.
- ingress-nginx retired: proxy annotation equivalentsAnnotation by annotation: what ingress2gateway emits, the Gateway API field, Envoy Gateway policy and Traefik's ingress-nginx provider, with the defaults that change.
- nginx proxy_pass and the trailing slashThe rule that decides the upstream URI. proxy_pass with a URI part replaces the matched location prefix; without one it forwards the request URI as sent.
- nginx redirect drops the port or scheme: which fixnginx builds redirects from its own listen port and scheme, not the client's. Tested Location headers for absolute_redirect, port_in_redirect, proxy_redirect.
- nginx as a reverse proxy: a complete configurationServer selection, location precedence, the header block every proxy needs, upstream tuning and reload semantics, and the defaults that break modern backends.
- nginx vs HAProxy vs Envoy vs Caddy vs TraefikA decision framework, a 19-row capability matrix and a defaults table for choosing between nginx, HAProxy, Envoy, Caddy and Traefik, plus what each one is bad at.
- Sticky sessions and session affinityCookie insertion, source IP hashing and consistent hashing compared across five proxies, with the security, autoscaling and draining costs of affinity.
- Traefik routers, services and middlewaresHow Traefik entryPoints, routers, services, middlewares and providers fit together, v2 to v3 rule syntax changes, Docker label config, ACME storage and the failure modes.
- WebSockets through a reverse proxyThe RFC 6455 handshake, why proxies strip Upgrade and Connection, config for nginx, HAProxy, Caddy, Traefik and Envoy, and the timeouts that kill sockets.