Skip to main content

Nginx

Quick reference: Nginx Cheatsheet

Nginx is a high-performance web server. It is also often used as a reverse proxy, a load balancer, and the HTTPS termination point (SSL termination) in front of React, Next.js, Node.js, or Spring Boot apps.

Use Nginx to
  • Serve static files (an SPA build) quickly and with little overhead
  • Sit in front of a backend: route, hide internal ports, set headers
  • Spread load across several instances
  • Manage HTTPS, security headers, compression, caching, and rate limits in one place

Event-driven architecture​

Nginx does not start a new process for each request. A master process reads the config and manages the worker processes. Each worker runs an async event loop and handles thousands of connections at once, so it uses little RAM and holds up under load better than a process/thread-per-request model.

Reverse proxy by application type​

server {
listen 80;
server_name your_react_app.com;

root /var/www/your_react_app/build;
index index.html;

location / {
try_files $uri $uri/ /index.html;
}
}

The directive that matters is try_files $uri $uri/ /index.html;. Any request that matches neither a static file nor a directory returns index.html, so React Router can handle client-side routing.

Load balancing​

Declare the instances in an upstream block, then point proxy_pass http://backend; at it. A directive inside upstream selects the algorithm. Try the simulator below: Server 3 is 3 times slower. Compare Round Robin with Least Connections.

The default. Sends to each server in turn, ignoring how busy a server is, so a slow server piles up connections.

Server 1fast
0 active · 0 total
Server 2fast
0 active · 0 total
Server 3slow
0 active · 0 total
  1. No requests yet.
nginx.conf
upstream backend {
server 10.0.1.1:8080;
server 10.0.1.2:8080;
server 10.0.1.3:8080;
}
AlgorithmDirectiveWhen to use
Round Robin(default)Servers with similar specs, requests that finish about as fast
Least Connectionsleast_conn;Request processing times vary a lot
IP Haship_hash;You need session persistence (session stored in server memory)
Weightedserver ... weight=3;Servers differ in capacity
tip

ip_hash uses only the first 3 octets of an IPv4 address, so every client in the same /24 (for example, behind one company NAT) lands on one server. To spread them more evenly, use hash $remote_addr consistent; — or, better, store the session in Redis so you do not need a sticky session.

Deployment​

How you deployProsCons
Ubuntu Server (manual)Full control, deep tuning at the OS levelComplex, hard to keep consistent
Docker (Dockerfile)Packages Nginx plus its config; consistent and portableEach container is managed on its own
Docker ComposeManages the whole stack (frontend, backend, Nginx, DB)One host only

One full stack, where Nginx is the only entry point and routes by path:

docker-compose.yml
services:
frontend:
build: ./frontend
backend:
build: ./backend
nginx:
image: nginx:stable-alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- frontend
- backend
nginx.conf
server {
listen 80;

location /api/ {
proxy_pass http://backend:5000/;
}

location / {
proxy_pass http://frontend:3000;
}
}

On the Compose network, the service name (frontend, backend) is the hostname, so you do not need an IP. Only nginx publishes a port to the outside.

Security​

HTTPS with Let's Encrypt​

certbot obtains a free SSL certificate, edits the Nginx config, and renews it on a schedule:

sudo certbot --nginx -d your_domain.com

Redirect all HTTP traffic to HTTPS:

server {
listen 80;
server_name your_domain.com;
return 301 https://$host$request_uri;
}

HTTP security headers​

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; # Force HTTPS
add_header X-Frame-Options "SAMEORIGIN" always; # Block clickjacking
add_header X-Content-Type-Options "nosniff" always; # Block MIME sniffing
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
warning
  • Without always, the header is not sent on error responses (4xx, 5xx).
  • If a location has its own add_header, it does not inherit the parent block's add_header lines — declare them all again. From Nginx 1.29.3 (included in stable 1.30) you can use add_header_inherit merge; to merge the parent headers.

Performance tuning​

TechniqueDirectiveEffect
Cachingproxy_cache_path, proxy_cacheStore responses that are used often, cut backend load
Gzip compressiongzip on;, gzip_typesShrink HTML/CSS/JS so pages load faster
Rate limitinglimit_req_zone, limit_reqSlow brute-force and resource abuse
Workerworker_processes, worker_connectionsUse every CPU core and the concurrent connections
nginx.conf
worker_processes auto; # equal to the number of CPU cores

events {
worker_connections 1024;
}

http {
gzip on;
gzip_types text/css application/javascript application/json image/svg+xml; # text/html is always compressed

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=app_cache:10m max_size=1g inactive=60m;
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;

server {
location /api/ {
limit_req zone=api_limit burst=20 nodelay;
proxy_cache app_cache;
proxy_cache_valid 200 10m;
proxy_pass http://backend;
}
}
}

Management tools​

A text config is already enough, but a graphical UI is easier when you are starting out or you have many hosts.

ToolBest forNotes
Nginx Proxy ManagerProxy managementWeb UI for reverse proxy, with Let's Encrypt built in
PortainerDocker managementView logs; manage containers, images, volumes, and networks visually
WebminServer administrationLinux control panel, with modules for Nginx, users, firewall…