What I Didn’t Know About NGINX Until Production Taught Me
Most backend systems aren’t architected. They’re just deployed. And that gap is exactly where things break.
What I Didn’t Know About NGINX Until Production Taught Me

Most backend systems aren’t architected. They’re just deployed. And that gap is exactly where things break.
An unprotected endpoint getting hammered. A certificate expiring across six services. A slow upload tying up every worker. None of these are code problems. They’re infrastructure problems, solved before your app sees a single request.
Rate limiting is free. TLS offloading cuts compute. A proxy layer kills bad traffic at the edge.
That layer is NGINX. Not a web server. The thing between the internet and everything you care about: encryption, routing, malicious traffic, rate limits.
This is how you use it.
01. Reverse Proxy
Your application servers should never be exposed to the internet. NGINX sits in front, takes every request, and forwards it to the right backend.
Think of it as a front desk: clients never walk into the back office. They talk to reception, reception handles it.

This gives you backend isolation, centralized logic (headers, compression, CORS, logging), and request buffering. That last one matters more than people think.
Without NGINX, a user on a slow connection uploading a 10MB file holds your app worker hostage for the entire upload. NGINX buffers it, then delivers a clean completed request to your backend.
location / {
proxy_pass http://backend_cluster;
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_read_timeout 60s;
proxy_connect_timeout 5s;
}
The headers preserve the real client IP so your backend logs are actually useful. The timeouts prevent one flaky backend from cascading into a full outage.
02. TLS Termination
TLS is CPU-intensive. Having every backend service handle HTTPS independently wastes compute and turns certificate rotation into a multi-service operation. Which saves the compute cost.
Terminate at NGINX instead. Decrypt once at the edge, forward plain HTTP internally.

Client (HTTPS)
↓
NGINX ← decrypts here
↓
Backend (plain HTTP)
listen 443 ssl http2;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
add_header Strict-Transport-Security "max-age=31536000" always;
Bonus: enable HTTP/2 here and every client gets multiplexing even if your backends don’t support it. One certificate renewal, one NGINX reload. Done.
03. Load Balancing
One backend isn’t enough for real traffic. NGINX distributes across multiple nodes pick your algorithm based on what your API actually does.
Round Robin: Equal distribution. Fine for fast, uniform requests.
Least Connections: Routes to the least-busy server. Better for APIs with variable response times.
IP Hash: Same IP always hits the same server. Use when you need session stickiness.
upstream backend_cluster {
least_conn;
server 10.0.1.10:8000;
server 10.0.1.11:8000;
}
location / {
proxy_pass http://backend_cluster;
}
For WebSockets and long-running streaming requests,
least_conndramatically outperforms round-robin. Round-robin doesn't know which servers are already busy holding open connections.
04. Security Hardening
Your application should not be your first security boundary. NGINX filters and sanitizes before traffic ever reaches your code.
Start with response headers. These lines close entire vulnerability classes:
Header What it prevents X-Frame-Options: DENY Clickjacking X-Content-Type-Options: nosniff MIME sniffing X-XSS-Protection: 1; mode=block Reflected XSS Referrer-Policy: no-referrer Data leakage
# Hide what you're running
server_tokens off;
# Block unexpected HTTP methods
if ($request_method !~ ^(GET|POST|PUT|DELETE|PATCH|OPTIONS)$) {
return 405;
}
# Reject payload abuse
client_max_body_size 10M;
server_tokens off is a small change that stops NGINX from broadcasting its version number in every response header. Attackers use that to look up version-specific CVEs.
05. Rate Limiting
Bots don’t stop. Rate limiting is what keeps a scraper or brute-force attack from becoming a scaling incident.
# Define a zone — tracks request rate per IP, 10MB shared memory
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=5r/s;
# Apply it
location /api/ {
limit_req zone=api_limit burst=10 nodelay;
proxy_pass http://backend_cluster;
}
rate=5r/s :5 requests per second per IP burst=10: allows short traffic spikes, so legitimate users stay unaffected no delay: requests beyond burst are rejected immediately, not queued
Always rate-limit these endpoints like :/login, /otp, /password-reset, /register, /ai/inference
The Production Request Lifecycle
Every request through a properly configured NGINX layer goes through this before your app sees it:
Internet request arrives
↓
TLS decrypted at NGINX edge
↓
Rate limit checked — excessive traffic blocked
↓
Security headers attached to response
↓
Load balanced across healthy backend nodes
↓
Application code finally runs
The Real Lesson
Most production outages aren’t caused by bad code.
They’re caused by an unprotected login endpoint getting hammered. A certificate that expired across six services at once. A slow client upload that tied up every available worker.
None of that requires a code fix. It requires a properly configured proxy layer.
NGINX doesn’t make your application faster. It makes your application irrelevant to the chaos happening in front of it.
That’s the point.
Everything in this publication is made with care by a tight-knit team of engineers, dreamers, and problem-solvers at reverseBits. Peek into our world at
메타데이터
- post_id
- 883a2e702df0
- slug
- what-i-didnt-know-about-nginx-until-production-taught-me-883a2e702df0
- url
- https://medium.com/reversebits/what-i-didnt-know-about-nginx-until-production-taught-me-883a2e702df0
- canonical_url
- https://medium.com/reversebits/what-i-didnt-know-about-nginx-until-production-taught-me-883a2e702df0
- author_url
- https://medium.com/@hetvi.patoliya
- status
- ok
- fetched_at
- 2026-06-11 10:13:20