Nginx: proxy_pass, TLS, Upstreams and Rate Limiting Reference Guide
By DevShelfHub
Virtual hosts, proxy_pass, SSL/TLS with Let's Encrypt, rate limiting, gzip, caching, load balancing, and security headers — nginx reference for reverse proxies, API gateways, and static file servers.
101 items
◷ 8 min
server
location
upstream
Start hereQuick start · 6 you’ll reach for daily
Test confignginx -t
Reloadsystemctl reload nginx
Server blockserver { … }
Routelocation / { … }
Proxyproxy_pass http://…
Tailtail -F …/error.log
Target versions · paceVersions
Targets:nginx ≥ 1.24 (stable)HTTP/2 stable, HTTP/3 with quic moduleOpenSSL 3.x
Snippets target nginx 1.24+ on a Debian-style layout (/etc/nginx/sites-enabled).
HTTP/2 is one keyword (http2); HTTP/3 needs nginx built with
the QUIC module (1.25+) and uses listen 443 quic. Legacy
listen 443 ssl spdy; syntax is gone.
install · control · logsSetup
bash
# Install
apt install nginx # Debian / Ubuntu
brew install nginx # macOS dev
docker run -d -p 80:80 nginx:stable # one-shot
# Config layout (Debian)
/etc/nginx/nginx.conf # main; includes sites-enabled/*
/etc/nginx/sites-available/ # write configs here
/etc/nginx/sites-enabled/ # symlinks to sites-available
/etc/nginx/conf.d/ # alternative drop-in dir
/var/log/nginx/{access,error}.log # default logs
# Daily-loop
nginx -t # validate config; ALWAYS run before reload
systemctl reload nginx # apply config without dropping connections
systemctl restart nginx # full restart (drops in-flight)
nginx -s reload / -s stop # if not using systemd
nginx -V # version + compile flags + modules
# Tail logs
tail -F /var/log/nginx/{access,error}.log
how files fit togetherConfig structure
main context
Top-level: user, worker_processes, events, http.
events { worker_connections 1024; }
Per-worker connection cap.
http { … }
Where all HTTP servers live.
server { … }
A virtual host. Multiple per http.
location PATTERN { … }
URL-prefix routing inside a server.
upstream NAME { … }
Pool of backend servers.
map / geo / split_clients
Lookup-table directives, top-level in http.
include path/*.conf
Glob-load other files.
stream { … }
TCP/UDP proxying (separate from HTTP).
virtual hostsServer blocks
listen 80 / listen 443 ssl http2
Port + protocol. http2 implied SSL prior; now explicit.
1–9. 5 is a strong default; 9 burns CPU for little gain.
gzip_proxied any
Compress proxied responses too.
gzip_vary on
Add Vary: Accept-Encoding.
brotli on (with ngx_brotli)
Better compression ratios. Module not in stock nginx.
Static-precompressed: gzip_static on
Serve pre-built file.gz when present.
URLsRewrites
return 301 https://…
Preferred over rewrite for whole-URL redirects.
rewrite ^/old/(.*)$ /new/$1 permanent
301.
rewrite ^/old/(.*)$ /new/$1 redirect
302.
rewrite ^/old/(.*)$ /new/$1 last
Internal rewrite; re-evaluate location.
rewrite ^/old/(.*)$ /new/$1 break
Internal rewrite; continue in this location.
if ($host = www.example.com)
Use if only inside server for redirects. Avoid elsewhere.
$args / $arg_name
Query string and individual query params.
Avoid if in location blocks. It interacts badly
with other directives and the official guidance is “if is evil”. Use
map or split into multiple servers/locations.
Always nginx -t before reload.
A typo in production drops zero connections if you catch it pre-reload — or every connection if you
don’t. Wire it into your deploy script.
reload, not restart.
Reload swaps config without dropping in-flight requests. Restart kills connections. Only restart when
changing core worker settings or the binary.
Prefer map over if.map is evaluated at config time, side-effect-free, and composes. if
inside location creates a hidden nested location and breaks several other
directives.
Common trapsWatch out for
Trailing slash in proxy_pass changes semantics.proxy_pass http://api; forwards the full URI. proxy_pass http://api/;
strips the location prefix. Get this wrong and routes 404 silently.
if inside location — just don’t.
Some directives behave differently or are ignored inside if. The pinned
“If is Evil”
page documents which.
Default listen 443 can’t be reused on a different IP family without [::]:443.
A common “why is IPv6 not working” mystery. Always pair listen 443 with
listen [::]:443.
Nginx is a high-performance web server, reverse proxy, and load balancer. It serves static files directly, proxies requests to upstream application servers (Node.js, Django, Rails), handles TLS termination, rate limiting, caching, and HTTP/2 and HTTP/3 connections.
What is the difference between nginx server blocks and Apache virtual hosts?
Both map domain names to document roots. Nginx server blocks use the server { } directive and are event-driven with a single master and worker-process model, making it more efficient under high concurrency. Apache uses per-request threads or processes and supports .htaccess per-directory overrides.
How does proxy_pass work in nginx?
proxy_pass forwards an incoming request to an upstream server and returns the response to the client. Set proxy_set_header Host $host and proxy_set_header X-Real-IP $remote_addr to pass original request metadata. Use an upstream block to define a pool of backend servers for load balancing.
How do I configure TLS in nginx?
In the server block, add listen 443 ssl http2, ssl_certificate /path/to/cert.pem, and ssl_certificate_key /path/to/key.pem. Use Let's Encrypt with Certbot to automate certificate issuance and renewal. Always redirect port 80 to 443 with a return 301 directive.
How does nginx rate limiting work?
Define a shared memory zone with limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s in http { }. Apply it with limit_req zone=api burst=20 nodelay in a location block. The burst value allows a short spike above the rate; nodelay returns 503 immediately instead of queuing excess requests.
How do I set up HTTPS with Let's Encrypt and nginx?
Install Certbot (apt install certbot python3-certbot-nginx), then run certbot --nginx -d yourdomain.com. Certbot edits your nginx config to add ssl_certificate, ssl_certificate_key, and a 301 redirect from HTTP to HTTPS. Set up auto-renewal with a cron job or systemd timer: certbot renew --quiet runs every 60 days and reloads nginx automatically.