0% found this document useful (0 votes)
3 views39 pages

Nginx Complete Tutorial

This document is a comprehensive tutorial on Nginx tailored for Node.js developers, covering installation, configuration, and advanced features such as reverse proxy, load balancing, and security. It includes detailed explanations of directives, practical examples, and best practices for optimizing performance and managing static files. The tutorial also emphasizes the importance of Nginx in enhancing Node.js applications by handling tasks like SSL termination and caching.

Uploaded by

3rlgoyrak
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views39 pages

Nginx Complete Tutorial

This document is a comprehensive tutorial on Nginx tailored for Node.js developers, covering installation, configuration, and advanced features such as reverse proxy, load balancing, and security. It includes detailed explanations of directives, practical examples, and best practices for optimizing performance and managing static files. The tutorial also emphasizes the importance of Nginx in enhancing Node.js applications by handling tasks like SSL termination and caching.

Uploaded by

3rlgoyrak
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

NGINX

Beginner to Advanced
Complete Tutorial for [Link] Developers

Installation · Configuration · Reverse Proxy · Load Balancing


SSL/TLS · HTTP/2 · Caching · Security · Performance Tuning

Every directive explained — with why, how, and [Link] examples

Nginx Complete Tutorial — [Link] Developer Edition Page 1


■ Table of Contents
1. What is Nginx? Architecture & Core Concepts
2. Installation & First Steps
3. Nginx Configuration – Deep Dive (Every Directive)
4. Serving Static Files
5. Nginx as Reverse Proxy for [Link]
6. Load Balancing
7. SSL / TLS & HTTPS
8. HTTP/2 & HTTP/3
9. Caching
10. Security Hardening
11. Gzip & Brotli Compression
12. Logging & Monitoring
13. Rate Limiting & DDoS Protection
14. WebSockets with [Link]
15. Nginx with Docker & [Link]
16. Performance Tuning
17. Common Patterns & Best Practices
18. Troubleshooting Guide

Nginx Complete Tutorial — [Link] Developer Edition Page 2


1. What is Nginx? Architecture & Core
Concepts
Nginx (pronounced "engine-x") is a high-performance, open-source web server, reverse proxy, load
balancer, mail proxy, and HTTP cache. Created by Igor Sysoev in 2004, it was designed to solve the C10k
problem — handling 10,000 simultaneous connections efficiently.

Why Should a [Link] Developer Learn Nginx?


[Link] is excellent at application logic, but Nginx excels at:

• Serving static files (HTML, CSS, JS, images) much faster than Node
• Terminating SSL/TLS so your Node app handles plain HTTP internally
• Load balancing across multiple [Link] processes or servers
• Caching responses — reducing load on your [Link] app
• Protecting your [Link] app from direct internet exposure
• Rate limiting, DDoS mitigation, and request filtering
• WebSocket proxying with connection upgrade handling
• Gzip/Brotli compression without burdening Node

Event-Driven Architecture (Why Nginx is Fast)


Unlike Apache's thread-per-request model, Nginx uses an asynchronous, event-driven architecture.
One master process manages multiple worker processes. Each worker handles thousands of connections
using non-blocking I/O — very similar to how [Link] works internally.

Component Role

Master Process Reads config, manages workers, handles signals (reload/stop)

Worker Processes Handle actual client connections (1 per CPU core recommended)

Cache Manager Manages cache storage and expiry

Cache Loader Loads cache metadata from disk on startup

Nginx Complete Tutorial — [Link] Developer Edition Page 3


2. Installation & First Steps

Installation on Ubuntu / Debian


# Update package list

sudo apt update

# Install Nginx

sudo apt install nginx -y

# Start and enable Nginx

sudo systemctl start nginx

sudo systemctl enable nginx

# Check status

sudo systemctl status nginx

# Verify version

nginx -v

Installation on CentOS / RHEL / Amazon Linux


# Add Nginx repo

sudo yum install epel-release -y

sudo yum install nginx -y

# Start

sudo systemctl start nginx

sudo systemctl enable nginx

Installation via Docker (Common for [Link] devs)


# Pull official Nginx image

docker pull nginx:latest

# Run with config

docker run -d -p 80:80 -p 443:443 \

-v /path/to/[Link]:/etc/nginx/[Link] \

-v /path/to/certs:/etc/nginx/certs \

--name nginx nginx:latest

Nginx Complete Tutorial — [Link] Developer Edition Page 4


Key File & Directory Locations
Path Purpose

/etc/nginx/[Link] Main configuration file

/etc/nginx/conf.d/ Drop-in config files (*.conf loaded automatically)

/etc/nginx/sites-available/ Virtual host configs (Debian/Ubuntu)

/etc/nginx/sites-enabled/ Symlinks to active virtual hosts

/var/log/nginx/[Link] Access log (every request)

/var/log/nginx/[Link] Error log (problems & warnings)

/var/www/html/ Default webroot

/run/[Link] PID file of master process

/usr/share/nginx/html/ Default static files

Essential CLI Commands


# Test configuration syntax BEFORE reloading (always do this!)

sudo nginx -t

# Reload config without downtime (graceful)

sudo nginx -s reload

# Stop gracefully (waits for connections to finish)

sudo nginx -s quit

# Stop immediately

sudo nginx -s stop

# Show compiled modules and config

nginx -V

# Reload via systemctl

sudo systemctl reload nginx

■ Tip: Always run 'sudo nginx -t' before reloading. A bad config will crash Nginx on reload!

Nginx Complete Tutorial — [Link] Developer Edition Page 5


3. Nginx Configuration – Complete Deep
Dive
The Nginx configuration uses a hierarchical structure of contexts (blocks). Directives in outer contexts are
inherited by inner ones unless overridden. Understanding this hierarchy is the #1 key to mastering Nginx.

Config Structure Overview


# [Link] — top-level structure

# MAIN context (global settings)

user www-data;

worker_processes auto;

error_log /var/log/nginx/[Link] warn;

pid /run/[Link];

events {

# EVENT context — connection handling

worker_connections 1024;

http {

# HTTP context — all HTTP settings

server {

# SERVER (virtual host) context

location / {

# LOCATION context — URL matching

3.1 Main Context Directives


Directive / Option Default Description & When to Use
user nobody OS user/group that worker processes run as. Use 'www-data' (Debian) or 'nginx' (CentO

worker_processes 1 Number of worker processes. Set to 'auto' to match CPU cores. Each worker handles th

worker_rlimit_nofile — Max open file descriptors per worker. Increase for high-traffic (e.g., 65535). Must be >=

error_log logs/[Link] Path and level: debug, info, notice, warn, error, crit, alert, emerg. Use 'warn' in productio

pid logs/[Link] File that stores master process PID. Used by systemd and init scripts.

Nginx Complete Tutorial — [Link] Developer Edition Page 6


Directive / Option Default Description & When to Use
include — Include other config files. Example: include /etc/nginx/conf.d/*.conf;

daemon on Run in background. Set 'off' for Docker containers so the process stays in foreground.

env — Pass environment variables to Nginx (e.g., env TZ; to set timezone).

3.2 Events Context Directives


Directive / Option Default Description & When to Use
worker_connections 512 Max simultaneous connections per worker. Total = worker_processes × worker_connec

use auto Connection processing method. Linux: use 'epoll' (fastest). macOS: use 'kqueue'. Auto-

multi_accept off If 'on', worker accepts all new connections at once instead of one at a time. Better unde

accept_mutex on Serialize accept() calls across workers. Prevents thundering herd. Keep 'on' unless ben

3.3 HTTP Context Directives


Directive / Option Default Description & When to Use
include [Link] — Load MIME type mappings (text/html, image/png, etc.). Always include this!

default_type text/plain Default MIME type for unknown file extensions. Set to application/octet-stream for down

sendfile off Use kernel sendfile() syscall to serve files. MUCH faster than read()+write(). Always 'on

tcp_nopush off Send HTTP headers and file start in one TCP packet. Use with sendfile. Reduces packe

tcp_nodelay on Disable Nagle algorithm for keepalive connections. Reduces latency for small packets. K

keepalive_timeout 75s Time to keep idle keepalive connection open. 65-75s is good. Reduces TCP handshake

keepalive_requests 1000 Max requests per keepalive connection before closing. Increase for high-volume APIs.

client_max_body_size 1m Max request body size. Increase for file uploads (e.g., 50m for 50MB). Returns 413 if ex

client_body_timeout 60s Timeout waiting for client to send request body. Protect against slow-body attacks.

client_header_timeout 60s Timeout waiting for client to send full request headers.

send_timeout 60s Timeout between two successive writes to client. Protects against slow client attacks.

server_tokens on Show Nginx version in error pages and Server header. Set 'off' in production for security

types_hash_max_size 1024 Hash table size for MIME types. Increase to 2048 if you get warnings.

32
server_names_hash_bucket_size Hash bucket for server names. Increase to 64 or 128 for long domain names.

3.4 Server Context Directives


Directive / Option Default Description & When to Use
listen 80 Port to listen on. Examples: listen 443 ssl; listen [::]:80; (IPv6); listen 80 default_server;

server_name "" Hostname(s) this block handles. Use _ for catch-all. Supports wildcards (*.[Link]

root — Document root directory. Nginx prepends this to the URI to find files.

index [Link] Default file(s) to serve for directory requests. Nginx tries them in order.

error_page — Custom error pages. Example: error_page 404 /[Link]; error_page 500 502 503 504

access_log — Access log path and format for this server. Set 'off' to disable logging for specific vhosts

Nginx Complete Tutorial — [Link] Developer Edition Page 7


Directive / Option Default Description & When to Use
return — Send redirect or response directly. return 301 [Link] is the go-to red

rewrite — Rewrite URI using regex. Avoid overuse — prefer try_files or return for simple cases.

3.5 Location Context & Matching Rules


Location blocks match incoming request URIs. Nginx applies the most specific match. Order of priority:

Priority Syntax Example Description

1 (highest) = /path location = /[Link] Exact match. Stops searching immediately. Use for single files.

2 ^~ /path location ^~ /static/ Prefix match, no regex. Use for directories. Stops regex checking.

3 ~ regex location ~ \.php$ Case-sensitive regex. Order matters — first match wins.

3 ~* regex location ~* \.jpg$ Case-insensitive regex. First match among all regex wins.

4 (lowest) /path location /api/ Prefix match. Longest prefix wins among all prefix locations.

server {

# Exact match — highest priority

location = / {

return 200 'Root page hit exactly';

# Prefix match with no-regex flag — beats all regex

location ^~ /static/ {

root /var/www;

expires 1y;

# Case-insensitive regex — matches images

location ~* \.(jpg|jpeg|png|gif|webp|svg)$ {

expires 30d;

add_header Cache-Control 'public, immutable';

# Prefix fallback — [Link] API

location /api/ {

proxy_pass [Link]

# Catch-all

location / {

try_files $uri $uri/ /[Link];

3.6 try_files — The Most Important Location Directive

Nginx Complete Tutorial — [Link] Developer Edition Page 8


try_files is essential for [Link] SPAs and APIs. It tells Nginx to try serving files in sequence, falling back
to a URI or status code.

# Pattern: try the file → try as directory → fallback to [Link] (SPA)

location / {

root /var/www/my-react-app/dist;

try_files $uri $uri/ /[Link];

# 1. Try exact file ($uri = /about → /var/www/my-react-app/dist/about)


# 2. Try as directory ($uri/ = /about/ → look for [Link] inside)
# 3. Serve /[Link] — React Router handles routing client-side

# Return 404 if nothing matches

location /api/ {

try_files $uri $uri/ =404;

■ Tip: For React/Vue/Angular SPAs, always use try_files $uri $uri/ /[Link] so client-side routing works.

Nginx Complete Tutorial — [Link] Developer Edition Page 9


4. Serving Static Files
Nginx serves static files orders of magnitude faster than [Link] because it uses the OS kernel's sendfile()
syscall and handles caching at the network layer. Always offload static file serving to Nginx.

server {

listen 80;

server_name [Link] [Link];

root /var/www/html;

# Serve static files with aggressive caching

location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {

expires 1y;

add_header Cache-Control 'public, max-age=31536000, immutable';

access_log off; # Don't log static file requests

log_not_found off; # Don't log 404s for missing assets

# HTML files — short cache (changes often)

location ~* \.html$ {

expires 1h;

add_header Cache-Control 'public, max-age=3600';

# SPA fallback

location / {

try_files $uri $uri/ /[Link];

Alias vs Root
Both root and alias set the filesystem path, but they work differently:

# root: APPENDS the location path to root


# Request: /static/[Link] → /var/www/html/static/[Link]

location /static/ {

root /var/www/html;

# alias: REPLACES the location path entirely


# Request: /static/[Link] → /var/www/assets/[Link]
location /static/ {

Nginx Complete Tutorial — [Link] Developer Edition Page 10


alias /var/www/assets/; # Note trailing slash!

■ Note: Always add a trailing slash to alias paths. Missing it causes subtle 404 bugs.

Nginx Complete Tutorial — [Link] Developer Edition Page 11


5. Nginx as Reverse Proxy for [Link]
This is the most common use case for [Link] developers. Nginx sits in front of your Express/Fastify/Koa
app, handling SSL, static files, and proxying API requests to Node.

Basic Reverse Proxy Setup


# /etc/nginx/conf.d/[Link]

server {

listen 80;

server_name [Link];

location / {

proxy_pass [Link]

# Essential proxy headers

proxy_http_version 1.1;

proxy_set_header Upgrade $http_upgrade;

proxy_set_header Connection 'upgrade';

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;

proxy_cache_bypass $http_upgrade;

Complete Proxy Headers Reference


Directive / Option Default Description & When to Use
proxy_pass — Backend URL. Can be [Link] [Link] or unix socket ([Link]

proxy_http_version 1.0 HTTP version to use with backend. Set to 1.1 for keepalive and WebSockets. Required

proxy_set_header Host $proxy_host Passes the Host header. Use $host to forward original domain to [Link].


proxy_set_header X-Real-IP Passes real client IP. Read in [Link] via [Link]['x-real-ip'].


proxy_set_header X-Forwarded-For Appends client IP to chain. Standard way to forward IPs through proxies. Read via req.h


proxy_set_header X-Forwarded-Proto Passes original protocol (http/https). Lets Node know if request was HTTPS even thoug

proxy_connect_timeout 60s Timeout to establish connection to backend. 10-30s is reasonable.

proxy_send_timeout 60s Timeout to send request to backend. Increase for large uploads.

Nginx Complete Tutorial — [Link] Developer Edition Page 12


Directive / Option Default Description & When to Use
proxy_read_timeout 60s Timeout waiting for backend response. Increase for slow endpoints (reports, exports). V

proxy_buffering on Buffer backend response. 'on' = Nginx buffers whole response, then sends to client. 'off

proxy_buffer_size 4k Size of buffer for the first part of response (headers). Increase to 16k if headers are larg

proxy_buffers 8 4k Number and size of buffers for response body. Increase for large responses.

proxy_cache_bypass — Conditions to bypass cache. Common: proxy_cache_bypass $http_upgrade; (for WebS

proxy_next_upstream error timeout On which conditions to try next upstream server. Add non_idempotent with caution.

Trust X-Forwarded-For in [Link] (Express)


// In your Express app — trust Nginx proxy

const app = express();

[Link]('trust proxy', 1); // Trust first proxy (Nginx)

// Now [Link] will return real client IP

[Link]('/ip', (req, res) => {

[Link]({ ip: [Link] });

});

■ Tip: Always set 'trust proxy' in Express when behind Nginx. Otherwise [Link] returns [Link].

Nginx Complete Tutorial — [Link] Developer Edition Page 13


6. Load Balancing
Nginx can distribute traffic across multiple [Link] instances. This is how you scale horizontally — run
multiple Node processes and let Nginx distribute load.

Upstream Block & Load Balancing Methods


http {

# Define upstream group

upstream node_cluster {

# Default: round-robin (each request goes to next server in turn)

server [Link]:3001;

server [Link]:3002;

server [Link]:3003;

server [Link]:3004;

# Enable keepalive connections to backend

keepalive 32; # Keep 32 idle connections per worker

server {

listen 80;

location / {

proxy_pass [Link]

proxy_http_version 1.1;

proxy_set_header Connection ''; # Required for keepalive

Load Balancing Algorithms


Directive / Option Default Description & When to Use
(default — round-robin) — Requests distributed evenly in rotation. Good for stateless APIs with similar response tim

least_conn — Sends request to server with fewest active connections. Best when requests have varia

ip_hash — Same client IP always goes to same server (sticky sessions). Useful when session state

hash $request_uri — Hash based on URI. Same URL always hits same server. Useful for caching consistenc

random — Random server selection. Add 'two least_conn' to pick best of two random servers (pow

Nginx Complete Tutorial — [Link] Developer Edition Page 14


Directive / Option Default Description & When to Use
weight=N 1 Server weight. weight=3 means server gets 3x more requests. Use for servers with diffe

backup — Marks server as backup. Only used when primary servers are unavailable.

down — Marks server as permanently unavailable. Useful for maintenance.

max_fails=N 1 Number of failed attempts before marking server as unavailable. Combine with fail_time

fail_timeout=Ns 10s How long server is considered down after max_fails. And the time window in which failu

PM2 + Nginx — Production [Link] Setup


# Start 4 [Link] processes with PM2

pm2 start [Link] -i 4 --name 'api'

# PM2 assigns ports 3001–3004 automatically with cluster mode


# Or manually start on specific ports

pm2 start [Link] --name 'api-1' -- --port 3001

pm2 start [Link] --name 'api-2' -- --port 3002

pm2 start [Link] --name 'api-3' -- --port 3003

pm2 start [Link] --name 'api-4' -- --port 3004

# Nginx upstream config

upstream node_api {

least_conn;

server [Link]:3001 max_fails=3 fail_timeout=30s;

server [Link]:3002 max_fails=3 fail_timeout=30s;

server [Link]:3003 max_fails=3 fail_timeout=30s;

server [Link]:3004 max_fails=3 fail_timeout=30s;

keepalive 64;

Nginx Complete Tutorial — [Link] Developer Edition Page 15


7. SSL / TLS & HTTPS
Nginx handles SSL termination — it decrypts HTTPS traffic and forwards plain HTTP to your [Link] app.
This is the standard production pattern: your Node app never needs to deal with certificates.

Let's Encrypt with Certbot (Free SSL)


# Install Certbot

sudo apt install certbot python3-certbot-nginx -y

# Obtain and auto-configure certificate

sudo certbot --nginx -d [Link] -d [Link]

# Test auto-renewal

sudo certbot renew --dry-run

# Certbot adds a cron job to auto-renew. Check:

sudo systemctl status [Link]

Manual SSL Configuration (Full Config)


server {

listen 80;

server_name [Link] [Link];

# Redirect all HTTP to HTTPS

return 301 [Link]

server {

listen 443 ssl;

http2 on; # Enable HTTP/2 (Nginx 1.25.1+)

server_name [Link] [Link];

# Certificate files

ssl_certificate /etc/letsencrypt/live/[Link]/[Link];

ssl_certificate_key /etc/letsencrypt/live/[Link]/[Link];

# Modern SSL settings (TLS 1.2 + 1.3 only)

ssl_protocols TLSv1.2 TLSv1.3;

ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256

:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305

Nginx Complete Tutorial — [Link] Developer Edition Page 16


:DHE-RSA-AES128-GCM-SHA256;

ssl_prefer_server_ciphers off;

# SSL session cache — avoids full handshake on reconnect

ssl_session_cache shared:SSL:10m;

ssl_session_timeout 1d;

ssl_session_tickets off;

# OCSP Stapling — speeds up certificate verification

ssl_stapling on;

ssl_stapling_verify on;

resolver [Link] [Link] valid=300s;

resolver_timeout 5s;

# HSTS — tell browsers to always use HTTPS

add_header Strict-Transport-Security 'max-age=63072000' always;

location / {

proxy_pass [Link]

proxy_set_header X-Forwarded-Proto https;

Directive / Option Default Description & When to Use


ssl_protocols TLSv1 TLSv1.1.. Only allow TLS 1.2 and 1.3. Never enable SSLv3 or TLS 1.0/1.1 — they're insecure.

ssl_ciphers DEFAULT Cipher suites in OpenSSL format. Mozilla SSL Generator gives current best-practice list

ssl_prefer_server_cipherson Server chooses cipher, not client. Set 'off' for TLS 1.3 (it handles this itself).

ssl_session_cache none Shared SSL session cache between workers. 'shared:SSL:10m' stores ~40,000 session

ssl_session_timeout 5m How long SSL session parameters are cached. 1d reduces handshakes for returning cli

ssl_session_tickets on TLS session tickets for resumption. Set 'off' for forward secrecy (requires session cache

ssl_stapling off OCSP Stapling — Nginx fetches cert revocation status and attaches to handshake. Spe

ssl_dhparam — DH parameters file for DHE ciphers. Generate: openssl dhparam -out /etc/nginx/dhpara

Nginx Complete Tutorial — [Link] Developer Edition Page 17


8. HTTP/2 & HTTP/3

HTTP/2
HTTP/2 dramatically improves performance over HTTP/1.1 by using multiplexing (multiple requests over
one TCP connection), header compression (HPACK), and server push. Enable it — it requires no changes
to your [Link] code.

# Nginx 1.25.1+ (recommended)

server {

listen 443 ssl;

http2 on;

# ... rest of config

# Older Nginx (before 1.25.1)

server {

listen 443 ssl http2;

# ...

■ Tip: HTTP/2 requires HTTPS. Always enable it on your HTTPS server block — it's free performance.

HTTP/3 / QUIC (Nginx 1.25+)


server {

listen 443 ssl;

listen 443 quic reuseport; # HTTP/3 over UDP

http2 on;

server_name [Link];

ssl_certificate /path/to/[Link];

ssl_certificate_key /path/to/[Link];

ssl_protocols TLSv1.3; # HTTP/3 requires TLS 1.3

# Tell browsers HTTP/3 is available

add_header Alt-Svc 'h3=":443"; ma=86400';

Nginx Complete Tutorial — [Link] Developer Edition Page 18


9. Caching
Nginx can cache responses from your [Link] backend, drastically reducing load and latency. A cached
response is served without even touching [Link].

Proxy Cache Setup


# In http context — define cache zone

http {

proxy_cache_path /var/cache/nginx

levels=1:2 # Directory structure for cache files

keys_zone=app_cache:10m # 10MB for cache keys (metadata)

max_size=1g # Max total cache size on disk

inactive=60m # Remove items not accessed for 60m

use_temp_path=off; # Write directly to cache path (faster)

server {

listen 80;

location /api/ {

proxy_pass [Link]

# Enable caching

proxy_cache app_cache;

proxy_cache_key '$scheme$request_method$host$request_uri';

# Cache successful responses for 10 minutes

proxy_cache_valid 200 302 10m;

proxy_cache_valid 404 1m;

# Serve stale cache while revalidating (background refresh)

proxy_cache_use_stale error timeout updating

http_500 http_502 http_503 http_504;

proxy_cache_lock on; # Only one request rebuilds cache at a time

# Add cache status header (HIT/MISS/BYPASS)

add_header X-Cache-Status $upstream_cache_status;

# Don't cache POST/PUT/DELETE

proxy_cache_methods GET HEAD;

# Bypass cache for logged-in users (has auth cookie)

proxy_cache_bypass $cookie_auth_token;
proxy_no_cache $cookie_auth_token;

Nginx Complete Tutorial — [Link] Developer Edition Page 19


}

Directive / Option Default Description & When to Use


proxy_cache_path — Define where and how to store cache. Set levels, keys_zone (required), max_size, inac

proxy_cache off Name of cache zone to use (from proxy_cache_path). Required to enable caching for a

proxy_cache_key $scheme$proxy_host$request_uri
Unique key per cache entry. Include method, scheme, host, URI. Add $http_authorizatio

proxy_cache_valid — How long to cache responses by status code. Can have multiple lines: proxy_cache_va

proxy_cache_use_stale off Serve stale cache on error/timeout. 'updating' serves stale while a fresh copy is being fe

proxy_cache_lock off Queue duplicate requests for same uncached resource. First request fetches, others wa

proxy_cache_bypass — Conditions to skip cache lookup. $http_pragma (Pragma: no-cache), auth cookies, etc.

proxy_no_cache — Conditions to not store response in cache. Use same conditions as proxy_cache_bypas

add_header X-Cache-Status— Add $upstream_cache_status to response: HIT, MISS, BYPASS, EXPIRED, STALE, UP

Nginx Complete Tutorial — [Link] Developer Edition Page 20


10. Security Hardening

Essential Security Headers


server {

# Hide Nginx version from error pages and Server header

server_tokens off;

# Security headers

add_header X-Frame-Options 'SAMEORIGIN' always;

add_header X-Content-Type-Options 'nosniff' always;

add_header X-XSS-Protection '1; mode=block' always;

add_header Referrer-Policy 'strict-origin-when-cross-origin' always;

add_header Permissions-Policy 'camera=(), microphone=(), geolocation=()' always;

# Content Security Policy (adjust to your needs)

add_header Content-Security-Policy

"default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe


-inline';" always;

# HSTS — force HTTPS for 2 years, include subdomains

add_header Strict-Transport-Security 'max-age=63072000; includeSubDomains; preload'


always;

Blocking Bad Requests


server {

# Block common exploit scanners by user agent

if ($http_user_agent ~* (Nikto|sqlmap|masscan|zgrab|nmap)) {

return 403;

# Block requests with no Host header

if ($host = '') {

return 400;

# Deny access to hidden files (.htaccess, .git, .env)

location ~ /\. {
deny all;

Nginx Complete Tutorial — [Link] Developer Edition Page 21


return 404;

# Block access to backup and config files

location ~* \.(bak|sql|conf|ini|env|log|sh|key)$ {

deny all;

return 404;

# Restrict HTTP methods

if ($request_method !~ ^(GET|POST|PUT|PATCH|DELETE|OPTIONS|HEAD)$) {

return 405;

# Limit request body size (default 1m — increase for file upload APIs)

client_max_body_size 10m;

Nginx Complete Tutorial — [Link] Developer Edition Page 22


11. Gzip & Brotli Compression
http {

# Gzip compression

gzip on;

gzip_vary on; # Add Vary: Accept-Encoding header

gzip_proxied any; # Compress for all proxied requests

gzip_comp_level 6; # 1-9: higher = smaller but slower. 6 is sweet spot

gzip_buffers 16 8k;

gzip_http_version 1.1;

gzip_min_length 256; # Don't compress tiny responses

gzip_types

text/plain text/css text/xml text/javascript

application/json application/javascript application/xml

application/rss+xml application/atom+xml

image/svg+xml font/woff font/woff2;

■ Tip: Never gzip images (JPEG, PNG, WebP) — they're already compressed. Only compress text-based
content.

Brotli (Better than Gzip — Nginx module)


# Requires ngx_brotli module (compile Nginx with it or use OpenResty)

http {

brotli on;

brotli_comp_level 6;

brotli_types text/plain text/css application/json application/javascript

text/xml application/xml image/svg+xml;

Nginx Complete Tutorial — [Link] Developer Edition Page 23


12. Logging & Monitoring

Custom Log Format


http {

# Define custom log format with useful fields

log_format main '$remote_addr - $remote_user [$time_local] '

'"$request" $status $body_bytes_sent '

'"$http_referer" "$http_user_agent" '

'$request_time $upstream_response_time';

# JSON format (great for log aggregation tools like Datadog, Loki)

log_format json_log escape=json

'{"time": "$time_iso8601", '

'"remote_addr": "$remote_addr", '

'"method": "$request_method", '

'"uri": "$uri", '

'"status": $status, '

'"bytes": $body_bytes_sent, '

'"request_time": $request_time, '

'"upstream_time": "$upstream_response_time", '

'"user_agent": "$http_user_agent"}';

access_log /var/log/nginx/[Link] json_log;

error_log /var/log/nginx/[Link] warn;

Important Log Variables


Variable Description

$remote_addr Client IP address

$time_local Local time of the request

$request Full request line (method + URI + protocol)

$status HTTP response status code

$body_bytes_sent Bytes sent in response body (excludes headers)

$request_time Total request processing time (in seconds, with milliseconds)

$upstream_response_time Time [Link] took to respond. KEY metric for performance monitoring.

$upstream_cache_status Cache HIT/MISS/BYPASS — measure cache effectiveness

Nginx Complete Tutorial — [Link] Developer Edition Page 24


$http_referer Referer header value

$http_user_agent User-Agent header value

$ssl_protocol SSL/TLS protocol used (TLSv1.2, TLSv1.3)

$ssl_cipher Cipher suite used for SSL connection

$gzip_ratio Compression ratio achieved

Nginx Complete Tutorial — [Link] Developer Edition Page 25


13. Rate Limiting & DDoS Protection
Nginx rate limiting protects your [Link] backend from brute force attacks, scraping, and DDoS. It's much
more efficient than doing rate limiting in Node.

http {

# Define rate limit zones


# $binary_remote_addr = client IP (binary = smaller than string)
# zone=api:10m = 10MB memory for ~160,000 IPs
# rate=10r/s = 10 requests per second limit

limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;

limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;

limit_req_zone $binary_remote_addr zone=global:10m rate=100r/s;

# Connection limiting (concurrent connections per IP)

limit_conn_zone $binary_remote_addr zone=addr:10m;

server {

# Global rate limit

limit_req zone=global burst=200 nodelay;

limit_conn addr 20; # Max 20 concurrent connections per IP

location /api/ {

limit_req zone=api burst=20 nodelay;

# burst=20: allow spike up to 20 extra requests


# nodelay: process burst instantly (not queue with delay)

proxy_pass [Link]

location /api/auth/login {

# Strict limit on login endpoint

limit_req zone=login burst=5 nodelay;

limit_req_status 429; # Return 429 Too Many Requests

proxy_pass [Link]

Directive / Option Default Description & When to Use


limit_req_zone — Define a rate limit zone. Format: $key zone=name:size rate=N r/s|r/m. Define in http{} b

limit_req — Apply rate limit in server/location. zone= (required), burst= (queue size), nodelay (proce

limit_req_status 503 HTTP status returned when rate limited. Change to 429 (standard) or 444 (drop connec

limit_conn_zone — Define zone for counting concurrent connections per key.

Nginx Complete Tutorial — [Link] Developer Edition Page 26


Directive / Option Default Description & When to Use
limit_conn — Max simultaneous connections per key in the zone.

limit_conn_status 503 HTTP status returned when connection limit hit.

burst 0 Number of extra requests to queue/allow beyond the rate. Prevents rejecting legitimate

nodelay — Process burst requests immediately instead of spacing them out. Add when you have b

Nginx Complete Tutorial — [Link] Developer Edition Page 27


14. WebSockets with [Link]
WebSockets require a protocol upgrade from HTTP to WS. Nginx handles this transparently with the right
headers. Works with [Link], ws, and any WebSocket library.

# WebSocket proxy configuration

map $http_upgrade $connection_upgrade {

default upgrade;

'' close;

server {

listen 443 ssl;

server_name [Link];

location /[Link]/ {

proxy_pass [Link]

# WebSocket upgrade headers

proxy_http_version 1.1;

proxy_set_header Upgrade $http_upgrade;

proxy_set_header Connection $connection_upgrade;

proxy_set_header Host $host;

proxy_set_header X-Real-IP $remote_addr;

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

# Keep WebSocket connections alive

proxy_read_timeout 3600s; # 1 hour timeout for WS connections

proxy_send_timeout 3600s;

proxy_connect_timeout 7s;

# Disable buffering for real-time data

proxy_buffering off;

# REST API on same server

location /api/ {

proxy_pass [Link]

proxy_http_version 1.1;

proxy_set_header Host $host;

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

}
}

Nginx Complete Tutorial — [Link] Developer Edition Page 28


■ Note: WebSocket connections are long-lived. Set proxy_read_timeout to at least 3600s (1 hour) to prevent
Nginx from closing idle connections.

Nginx Complete Tutorial — [Link] Developer Edition Page 29


15. Nginx with Docker & [Link]

[Link] — Full Stack Setup


version: '3.8'

services:

nginx:

image: nginx:alpine

ports:

- '80:80'

- '443:443'

volumes:

- ./nginx/[Link]:/etc/nginx/[Link]:ro

- ./nginx/conf.d:/etc/nginx/conf.d:ro

- ./certs:/etc/nginx/certs:ro

- ./logs:/var/log/nginx

depends_on:

- api

restart: unless-stopped

networks:

- app-net

api:

build: ./api

environment:

- NODE_ENV=production

- PORT=3000

expose:

- '3000' # Only expose to Docker network, NOT to host

restart: unless-stopped

networks:

- app-net

networks:

app-net:

driver: bridge

Nginx Config for Docker (conf.d/[Link])

Nginx Complete Tutorial — [Link] Developer Edition Page 30


upstream node_api {

server api:3000; # 'api' = docker-compose service name

keepalive 32;

server {

listen 80;

server_name _;

location / {

proxy_pass [Link]

proxy_http_version 1.1;

proxy_set_header Connection '';

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;

■ Tip: In Docker, use the service name (e.g., 'api') as the hostname in proxy_pass. Docker DNS resolves
service names automatically.

Nginx Complete Tutorial — [Link] Developer Edition Page 31


16. Performance Tuning
# [Link] — Optimized production config

user www-data;

worker_processes auto; # Match CPU cores

worker_rlimit_nofile 65535; # Max open files per worker

error_log /var/log/nginx/[Link] warn;

pid /run/[Link];

events {

worker_connections 4096; # Per worker. Total = cores * 4096

use epoll; # Linux kernel epoll (fastest)

multi_accept on; # Accept all connections at once

http {

sendfile on;

tcp_nopush on;

tcp_nodelay on;

# Keepalive

keepalive_timeout 65;

keepalive_requests 10000;

# Open file cache — cache frequently accessed file descriptors

open_file_cache max=200000 inactive=20s;

open_file_cache_valid 30s;

open_file_cache_min_uses 2;

open_file_cache_errors on;

# Buffers

client_body_buffer_size 16k;

client_header_buffer_size 1k;

client_max_body_size 10m;

large_client_header_buffers 4 8k;

# Timeouts

client_body_timeout 12;

client_header_timeout 12;

send_timeout 10;

# Gzip
gzip on;

Nginx Complete Tutorial — [Link] Developer Edition Page 32


gzip_comp_level 6;

gzip_types text/plain text/css application/json application/javascript;

include /etc/nginx/conf.d/*.conf;

Directive / Option Default Description & When to Use


open_file_cache off Cache file descriptors, sizes, and modification times. Prevents repeated open() syscalls

open_file_cache_valid 60s How often to revalidate cached file info. 30s is good for frequently-changing content.

open_file_cache_min_uses 1 Min accesses in inactive window before caching. Set to 2 to avoid caching rarely-access

open_file_cache_errors off Cache errors (file not found). Reduces stat() calls for missing files. Set 'on' in production

client_body_buffer_size 8k|16k Buffer for reading client request body. If body > this, Nginx writes to temp file. Match to

4 8k
large_client_header_buffers Buffer for large request headers (cookies, JWTs). Increase if clients send large headers

Nginx Complete Tutorial — [Link] Developer Edition Page 33


17. Common Patterns & Best Practices

Pattern 1: SPA (React/Vue/Angular) + [Link] API


server {

listen 443 ssl;

http2 on;

server_name [Link];

# Serve SPA static files

root /var/www/myapp/dist;

index [Link];

# Static assets — aggressive caching

location ~* \.(js|css|png|jpg|svg|woff2?)$ {

expires 1y;

add_header Cache-Control 'public, immutable';

access_log off;

# API — proxy to [Link]

location /api/ {

proxy_pass [Link]

proxy_http_version 1.1;

proxy_set_header Host $host;

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

proxy_set_header X-Forwarded-Proto $scheme;

# SPA fallback — client-side routing

location / {

try_files $uri $uri/ /[Link];

Pattern 2: Multiple [Link] Apps on One Server


# [Link]
server {

listen 443 ssl;

Nginx Complete Tutorial — [Link] Developer Edition Page 34


server_name [Link];

location / { proxy_pass [Link] }

# [Link]

server {

listen 443 ssl;

server_name [Link];

location / { proxy_pass [Link] }

# [Link]

server {

listen 443 ssl;

server_name [Link];

location / { proxy_pass [Link] }

Pattern 3: Serve [Link] App


server {

listen 443 ssl;

http2 on;

server_name [Link];

# Proxy everything to [Link]

location / {

proxy_pass [Link]

proxy_http_version 1.1;

proxy_set_header Upgrade $http_upgrade;

proxy_set_header Connection 'upgrade';

proxy_set_header Host $host;

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

proxy_set_header X-Forwarded-Proto $scheme;

proxy_cache_bypass $http_upgrade;

# Cache [Link] static assets from _next/static

location /_next/static/ {

proxy_pass [Link]

expires 1y;

add_header Cache-Control 'public, immutable';


}

Nginx Complete Tutorial — [Link] Developer Edition Page 35


}

Nginx Complete Tutorial — [Link] Developer Edition Page 36


18. Troubleshooting Guide

■ 502 Bad Gateway


Cause: [Link] is not running or crashed. Check: pm2 status, node [Link]. Verify port matches
proxy_pass.

# Fix:

curl [Link] # Test Node directly

■ 504 Gateway Timeout


Cause: [Link] is too slow to respond. Increase proxy_read_timeout or optimize slow endpoints.

# Fix:

proxy_read_timeout 120s;

■ 413 Request Entity Too Large


Cause: Request body exceeds client_max_body_size. Increase it for file upload endpoints.

# Fix:

client_max_body_size 50m;

■ 431 Request Header Fields Too Large


Cause: Headers too big (JWT tokens, cookies). Increase large_client_header_buffers.

# Fix:

large_client_header_buffers 4 16k;

■ ERR_CONTENT_LENGTH_MISMATCH
Cause: Proxy buffering issue with streaming responses. Disable proxy_buffering for SSE/streaming.

# Fix:

proxy_buffering off;

■ WebSocket 101 not received


Cause: Missing Upgrade/Connection headers. Add proxy_http_version 1.1 and the Upgrade/Connection
headers.

# Fix:

proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade;

Nginx Complete Tutorial — [Link] Developer Edition Page 37


■ CORS errors
Cause: Add CORS headers in Nginx or let your [Link] app handle them (preferred).

# Fix:

add_header Access-Control-Allow-Origin '*' always;

■ Config syntax error


Cause: Run nginx -t to check syntax before reloading. Read the error line number carefully.

# Fix:

sudo nginx -t && sudo nginx -s reload

■ Permission denied on log/cache files


Cause: Nginx worker runs as www-data. Ensure directories are owned by www-data.

# Fix:

chown -R www-data:www-data /var/cache/nginx

■ Redirect loop (HTTP<->HTTPS)


Cause: X-Forwarded-Proto not set or [Link] re-redirecting to HTTP.

# Fix:

proxy_set_header X-Forwarded-Proto $scheme;

Nginx Complete Tutorial — [Link] Developer Edition Page 38


Quick Reference — Nginx Cheat Sheet
Task Command / Config

Test config sudo nginx -t

Reload gracefully sudo nginx -s reload

Check status sudo systemctl status nginx

View access logs tail -f /var/log/nginx/[Link]

View error logs tail -f /var/log/nginx/[Link]

Check compiled modules nginx -V 2>&1 | grep --color -o '\-\-with[^ ]*'

Follow reload in log journalctl -u nginx -f

Redirect HTTP to HTTPS return 301 [Link]

Proxy to [Link] proxy_pass [Link]

Enable HTTP/2 listen 443 ssl; http2 on;

Free SSL cert certbot --nginx -d [Link]

Rate limit 10 req/s limit_req zone=api burst=20 nodelay;

Enable Gzip gzip on; gzip_comp_level 6;

SPA fallback try_files $uri $uri/ /[Link];

Block dotfiles location ~/\. { deny all; }

Cache static 1 year expires 1y; add_header Cache-Control 'public, immutable';

Disable server version server_tokens off;

You now have everything needed to go from zero to production-grade Nginx mastery. Every
directive is documented with its purpose, default, and [Link] context. Start with the reverse
proxy chapter, layer in SSL, then add caching and rate limiting. Good luck! ■

Nginx Complete Tutorial — [Link] Developer Edition Page 39

You might also like