The Nginx reverse proxy patterns we actually run in production
May 18, 2026 · 1 min read · by Sudhanshu K.
Most Nginx reverse-proxy config in production is a copy-paste from someone's blog circa 2017. It works, sort of. Keep-alive to the backend is off, so every request opens a new connection. The X-Forwarded-For chain is wrong, so the application logs the wrong client IP. proxy_buffering is on by default, which adds latency on streamed responses. None of these are dramatic failures — they're slow, steady tax that you only notice when you sit down and read what you've deployed.
This is the reverse-proxy config we copy onto every customer's edge.
The minimum-viable upstream block
upstream app {
server 10.0.1.10:8080 max_fails=3 fail_timeout=15s;
server 10.0.1.11:8080 max_fails=3 fail_timeout=15s;
keepalive 32;
keepalive_requests 1000;
keepalive_timeout 60s;
}
location / {
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Connection "";
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;
proxy_read_timeout 60s;
}The keepalive 32 + Connection "" is non-negotiable. Without it, you re-handshake TCP to the backend on every request — and for high-RPS internal traffic that doubles latency for no reason.
The full write-up covers:
- The X-Forwarded-For chain — and the
real_ip_headerblock that makes the app see the right IP proxy_bufferingvs streaming responses (Server-Sent Events, large downloads)- Health checks: passive (
max_fails/fail_timeout) vs active (commercial Nginx Plus orngx_http_upstream_check_module) proxy_next_upstream— when retries help and when they cause duplicate writes- Body size limits (
client_max_body_size) and where they bite uploads - The TLS-to-backend pattern when you actually want it
We ship this config on every managed Nginx install.
Full article available
Read the full article