Nginx sits in front of a huge share of the web's production traffic, and most developers only interact with its configuration in the exact moment something's broken and they're grepping through an unfamiliar config file under pressure.
Nginx is a high-performance web server and reverse proxy, commonly deployed in front of an application server to handle SSL termination, load balancing across multiple backend instances, static file serving, and request routing. Understanding its core configuration patterns — even if you're deploying primarily to managed platforms most of the time — matters the moment you're running any self-managed server infrastructure.
Why Nginx Matters (and When to Skip It)
Application servers (Node.js, Python, etc.) are generally not built to handle raw internet traffic directly — SSL termination, connection handling at scale, and static file serving are better handled by a purpose-built layer in front of them. Nginx does all of this efficiently and has decades of production hardening behind it, making it the default reverse proxy choice for self-managed deployments.
Skip direct Nginx configuration if you're deploying entirely to managed platforms (Vercel, Railway, Fly.io) that handle this layer for you — there's no reason to hand-configure a reverse proxy when your platform already provides equivalent functionality.
Getting Started with Nginx Configuration
A basic reverse proxy configuration:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://localhost:3000;
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;
}
}
Adding SSL termination with Let's Encrypt certificates:
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
proxy_pass http://localhost:3000;
}
}
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
Core Nginx Concepts Every Developer Should Know
Load balancing distributes traffic across multiple backend instances, essential for any deployment running more than one instance of your application:
upstream app_servers {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
}
server {
location / {
proxy_pass http://app_servers;
}
}
Static file serving directly from Nginx is far faster than proxying through your application server, since it avoids the overhead of the application runtime for content that doesn't need it:
location /static/ {
root /var/www/app;
expires 30d;
add_header Cache-Control "public, immutable";
}
Proxy headers preserve real client information that would otherwise be lost behind the proxy — X-Forwarded-For and X-Real-IP let your application server know the actual client IP rather than seeing every request as coming from Nginx itself.
Rate limiting protects against abuse and overload at the proxy layer, before requests even reach your application:
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
location /api/ {
limit_req zone=api_limit burst=20 nodelay;
proxy_pass http://localhost:3000;
}
Common Nginx Mistakes and How to Fix Them
Mistake 1: missing proxy headers, causing the application to see every request as coming from the same internal IP. This breaks IP-based rate limiting, geolocation, and logging in the application itself. Fix: always set X-Real-IP, X-Forwarded-For, and X-Forwarded-Proto in proxy configurations.
Mistake 2: no rate limiting at the proxy layer, leaving the application server exposed to being overwhelmed by traffic spikes or abuse directly. Fix: configure request rate limiting in Nginx as a first line of defense, in addition to any application-level limiting.
Mistake 3: not testing configuration changes before reloading. A syntax error in a live Nginx config can take down every site it serves. Fix: always run nginx -t to validate configuration syntax before reloading, as a non-negotiable step.
nginx -t && systemctl reload nginx
When Should You Configure Nginx Directly Instead of Using a Managed Platform?
Configure Nginx directly when you're self-hosting on your own servers or VPS and need reverse proxying, load balancing, or SSL termination that a managed platform doesn't provide. Use a managed platform's built-in equivalent (Vercel's edge network, Railway's proxying) when you're not otherwise managing your own server infrastructure — there's no benefit to manually replicating what the platform already handles.
Nginx Configuration in Production
Always validate configuration with nginx -t before every reload, and keep configuration files in version control with the same review process as application code. Also monitor Nginx's own access and error logs as a first diagnostic step for production issues — a surprising number of "application" issues actually show up clearly at the proxy layer first.
If you're self-hosting and haven't set up rate limiting or proper proxy headers yet, both are quick, high-value additions worth making before the next deploy.