Nginx as a reverse proxy is the backbone of reliable web traffic routing. Learn how to configure your setup line-by-line for production-grade performance.

I spent my first year in production engineering treating Nginx configs like black boxes. I’d copy-paste snippets from Stack Overflow, pray they didn't crash the server, and move on. It worked—until it didn't. When a production spike hit and my upstream services started timing out, I realized I didn't actually know how to tune the proxy layer.
Nginx as a reverse proxy is more than just a proxy_pass directive. It’s about how you hand off connection state, handle buffering, and manage timeouts. If you’re running a Zero-downtime deploy with GitHub Actions: A practical guide, your proxy is the gatekeeper that keeps users from seeing 502 Bad Gateway errors while your containers swap.
Let’s look at a standard configuration for a Node.js or Go service running on port 3000. We’ll place this inside a server block.
NGINXlocation / { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; }
Here is what is actually happening under the hood:
proxy_pass: This is your target. Nginx resolves the address and forwards the request. If you're using Docker, you can use the service name here instead of an IP (e.g., http://my-api:3000).proxy_http_version 1.1: By default, Nginx uses HTTP/1.0 for upstream connections, which doesn't support keep-alive. Setting this to 1.1 allows Nginx to reuse connections to your backend, which saves roughly 2-5ms per request by skipping the TCP handshake overhead.proxy_set_header Host $host: This is critical. Without it, your backend app might think it's being accessed by its own internal IP rather than the domain name the user requested. If your app relies on virtual hosts, this line is non-negotiable.One of the biggest mistakes I see is failing to pass the user's real IP to the application. If you don't configure this, every request to your backend will look like it's coming from 127.0.0.1.
You need these lines to maintain visibility into your logs:
NGINXproxy_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;
When you use Nginx as a reverse proxy, it acts as a man-in-the-middle. By passing these headers, you’re telling your application, "Hey, the request actually started here." If you’re ever curious about how this compares to Kubernetes-native routing, I’ve written about Kubernetes Ingress: NGINX vs Gateway API for traffic routing to help you decide when to stick with Nginx and when to upgrade your infrastructure.
By default, Nginx will buffer the entire response from your backend before sending it to the client. This is great for slow clients—it frees up your backend worker immediately.
However, if you are streaming large files or using Server-Sent Events (SSE), buffering is the enemy. It will cause your connection to hang until the buffer is full. If you’re building an application where real-time data is key, you’ll need to disable it:
NGINXproxy_buffering off; proxy_request_buffering off;
I once spent about two days debugging a "frozen" dashboard update, only to realize Nginx was patiently waiting to fill a 1MB buffer before pushing the data to the browser. Disabling the buffer fixed it instantly.
Production traffic is messy. If your backend takes 60 seconds to process a report, but Nginx has a default timeout of 60 seconds, you’re going to have a bad time. You should define your timeouts explicitly:
NGINXproxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s;
Don't just set these to infinity. If you have an unresponsive service, you want the proxy to fail fast so you can recover, rather than hanging your entire worker pool.
Q: Should I use Nginx or just expose my container ports directly? A: Always use a proxy. Nginx handles SSL termination, request buffering, and static file serving much more efficiently than most application frameworks.
Q: Why do I see 502 errors after a deployment?
A: Usually, this means Nginx is trying to connect to a socket that doesn't exist yet or has closed. Check your upstream configuration and ensure your backend is actually listening on the port you specified.
Q: Does using Nginx as a reverse proxy slow things down? A: The overhead is negligible—usually less than 1ms. The security and flexibility benefits far outweigh the cost.
Configuring Nginx is a skill that evolves with your infrastructure. I still occasionally misconfigure a header or forget a timeout, and it usually happens during a high-traffic window. The best advice I can give is to keep your configs modular. Don't build one giant nginx.conf; use include directives to keep your logic clean.
What’s the one Nginx directive you’ve struggled with the most? We’ve all been there, staring at a config file at 3 AM. It’s part of the process.
Linux server hardening doesn't have to be manual. Learn to use Lynis for automated security audits and fail2ban to block brute-force attacks effectively.