A living curriculum for mastering Nginx from the ground up
8
Modules
20
Sections
25%
Complete
2026-05-02
Last Updated
Module 01
⚡
Introduction
What Nginx is, why it matters, and how it fits into modern infrastructure.
basicsoverview
What is Nginx?
Nginx (pronounced 'engine-x') is a high-performance, open-source web server, reverse proxy, load balancer, and HTTP cache. Created by Igor Sysoev in 2004 to solve the C10K problem — handling 10,000+ concurrent connections — it uses an event-driven, asynchronous architecture instead of the traditional thread-per-request model.
💡Nginx powers over 34% of all websites. Understanding its async model is key to everything else.
basicscomparison
Nginx vs Apache
Apache uses a process/thread-per-request model (MPM). Nginx uses a single-threaded event loop with non-blocking I/O. Under high concurrency, Nginx uses significantly less memory and handles more requests per second. Apache excels at .htaccess flexibility and certain legacy PHP setups. Nginx wins on static file serving and as a reverse proxy.
💡For modern stacks (Node.js, Python, PHP-FPM), Nginx is almost always the better frontend.
Apache vs Nginx memory under loadtext
# 1000 concurrent connections
Apache: ~300MB RAM (process/thread per request)
Nginx: ~2.5MB RAM (event-driven, single thread per worker)
setupinstall
Installation
Nginx is available in most Linux package managers. The stable branch is recommended for production. The mainline branch gets new features faster but is less battle-tested.
💡Always check `nginx -v` after install. Use `systemctl enable nginx` to start on boot.
Install on Ubuntu/Debianbash
sudo apt update
sudo apt install nginx -y
# Start and enable
sudo systemctl start nginx
sudo systemctl enable nginx
# Check status
sudo systemctl status nginx
nginx -v
# nginx version: nginx/1.24.0
nginx -t
# nginx: configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful
setupfilesystem
File Structure
Nginx has a well-defined directory layout. Knowing where everything lives is essential before you start editing configs.
💡On Debian/Ubuntu the main config is in /etc/nginx/. On RHEL it's the same. Logs go to /var/log/nginx/.
Nginx filesystem layouttext
/etc/nginx/
├── nginx.conf # Main config entry point
├── conf.d/ # Additional config files (*.conf)
├── sites-available/ # Virtual host definitions (Debian style)
├── sites-enabled/ # Symlinks to active sites
├── snippets/ # Reusable config fragments
├── mime.types # MIME type mappings
└── fastcgi_params # FastCGI parameters
/var/log/nginx/
├── access.log # All HTTP requests
└── error.log # Errors and warnings
/var/www/html/ # Default web root
/run/nginx.pid # PID file
+Add Section
Module 02
🧠
Core Concepts
The mental model: processes, directives, contexts, and configuration syntax.
architectureprocesses
Master & Worker Processes
Nginx runs as a master process and one or more worker processes. The master reads config, manages workers, and handles signals. Workers handle actual client connections. Each worker is single-threaded and uses non-blocking I/O via epoll/kqueue to handle thousands of connections simultaneously.
💡The master process runs as root; workers run as www-data or nginx. Never run workers as root.
Check running processesbash
ps aux | grep nginx
# root 12345 nginx: master process /usr/sbin/nginx
# www-data 12346 nginx: worker process
# www-data 12347 nginx: worker process
Nginx config is made of directives. Simple directives end with a semicolon. Block directives (contexts) wrap other directives in curly braces. The main contexts are: main, events, http, server, location, upstream.
💡Inheritance: directives in outer contexts are inherited by inner contexts, but can be overridden. Always run `nginx -t` before reloading.
Basic nginx.conf structurenginx
# main context
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;
events {
worker_connections 1024;
use epoll; # Linux: epoll | macOS: kqueue
multi_accept on;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
# Include all server blocks
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
configdirectives
Directives & Contexts
Every Nginx behavior is controlled by directives placed in specific contexts. Understanding which directive belongs in which context prevents cryptic errors. Some directives are only valid in http{}, others only in server{} or location{}.
💡If Nginx throws 'directive not allowed here', you have a context mismatch.
The heart of Nginx: defining virtual hosts, name-based routing, and multi-site setups.
server-blockvirtualhost
Basic Server Block
A server block defines how Nginx responds to requests for a given domain, IP, or port. It's analogous to Apache's VirtualHost. At minimum, you need listen, server_name, and a root.
💡Always include both IPv4 and IPv6 listen directives. Use a trailing slash on root, never on alias.
# Save config to sites-available
sudo nano /etc/nginx/sites-available/example.com
# Create symlink to enable
sudo ln -s /etc/nginx/sites-available/example.com \
/etc/nginx/sites-enabled/
# Test and reload
sudo nginx -t && sudo systemctl reload nginx
server-blockmulti-site
Multiple Server Blocks
Nginx can host multiple sites on one machine using name-based virtual hosting. Each server block handles a different domain. Nginx matches the request's Host header against server_name values to pick the right block.
💡You can have unlimited server blocks. Name-based is preferred over IP-based for shared hosting.
When a request doesn't match any server_name, Nginx falls back to the 'default_server'. If none is explicitly marked, Nginx uses the first server block in config order. A catch-all block is important for security — it prevents unknown Host headers from leaking content.
💡Always define a default_server that returns 444 (no response) or a generic page. This stops host header attacks.
Secure catch-all default servernginx
server {
listen 80 default_server;
listen [::]:80 default_server;
# Drop connections with unknown Host header
return 444;
}
# Or: show a generic page
server {
listen 80 default_server;
server_name _;
root /var/www/default;
location / {
return 200 'Nothing here.';
add_header Content-Type text/plain;
}
}
server-blockmatching
Server Name Matching Order
Nginx matches server_name in a specific priority order: 1) Exact name match, 2) Longest wildcard starting with *, 3) Longest wildcard ending with *, 4) First matching regex (in config order), 5) default_server.
💡Regex server names start with ~ and use PCRE. They're slowest — use exact matches where possible.
1. = exact match
2. ^~ prefix (stops regex search)
3. ~ and ~* regex (first match wins)
4. Longest prefix match
5. / catch-all
locationtry_files
try_files
try_files checks for the existence of files in order and serves the first one found. It's essential for SPAs, WordPress, and clean URL routing. The last argument is always a fallback (URI or =CODE).
💡$uri tries the exact path. $uri/ tries as a directory (looking for index). =404 returns a 404 if nothing matches.
Proxying requests to backends — Node.js, Python, PHP-FPM, and load balancing.
proxyproxy_pass
Basic Reverse Proxy
Nginx sits in front of your app server and forwards requests. This enables SSL termination, caching, logging, and rate limiting at the proxy layer — keeping your app simple.
💡Always pass Host, X-Real-IP, and X-Forwarded-For headers to backends. Without them, your app won't see the real client IP.
Nginx can distribute requests across multiple backend servers using upstream blocks. Built-in methods: round-robin (default), least_conn, ip_hash, and hash. The upstream block lives in the http{} context.
💡Use `least_conn` for long-lived connections. `ip_hash` for session persistence. Add `backup` servers for failover.
Upstream load balancingnginx
upstream app_servers {
least_conn; # Method: least connections
server 10.0.0.1:3000 weight=3; # Gets 3x traffic
server 10.0.0.2:3000 weight=1;
server 10.0.0.3:3000 backup; # Only used if others fail
keepalive 32; # Keep connections open
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://app_servers;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
+Add Section
Module 06
🔒
SSL / TLS
HTTPS setup, Let's Encrypt, TLS best practices, and HTTP to HTTPS redirects.
sslcertbotletsencrypt
Let's Encrypt with Certbot
Certbot automates TLS certificate issuance and renewal from Let's Encrypt. The Nginx plugin handles both obtaining certificates and automatically modifying your server block config.
💡Certbot auto-renews via a systemd timer or cron. Always test renewal: `certbot renew --dry-run`.
Gzip, caching, worker tuning, and static asset optimization.
performancegzip
Gzip Compression
Gzip compresses HTTP responses before sending, reducing bandwidth by 60-80% for text-based content. Configure it in the http{} block to apply globally, or in specific server/location blocks.
💡Don't gzip images, videos, or already-compressed files — it wastes CPU with no size benefit.
Browsers cache static assets (CSS, JS, images) based on Cache-Control and Expires headers. Nginx can set these per file type. Content-addressed filenames (with hashes) allow aggressive long-term caching.
💡Use `immutable` for hashed filenames (e.g., app.a3f9d2.js). Never cache HTML aggressively — it's your cache-busting entry point.
Rate limiting, access control, security headers, and hardening your Nginx setup.
securityrate-limit
Rate Limiting
Rate limiting protects your server from abuse, brute force attacks, and DDoS. Nginx uses the leaky bucket algorithm. Define zones in http{} and apply them in server/location blocks.
💡Start lenient, then tighten. Use `limit_req_status 429` to return proper HTTP 429 Too Many Requests.
Rate limiting setupnginx
http {
# Define zones (in http context)
# 10mb zone stores ~160,000 IP states
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
limit_req_status 429;
}
server {
# General rate limit
location / {
limit_req zone=general burst=20 nodelay;
}
# Strict limit on login endpoint
location /login {
limit_req zone=login burst=3;
proxy_pass http://backend;
}
}
securityheaders
Security Headers
HTTP security headers instruct browsers on how to handle your content. They prevent XSS, clickjacking, MIME sniffing, and information leakage. Add them to your HTTPS server block.
💡Use https://securityheaders.com to audit your headers. Start with report-only for CSP before enforcing.
Essential security headersnginx
server {
# Hide Nginx version from error pages and headers
server_tokens off;
# Prevent clickjacking
add_header X-Frame-Options "SAMEORIGIN" always;
# Prevent MIME type sniffing
add_header X-Content-Type-Options "nosniff" always;
# XSS protection (legacy, but still useful)
add_header X-XSS-Protection "1; mode=block" always;
# Referrer policy
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Permissions policy
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
# Content Security Policy (customize for your app)
add_header Content-Security-Policy
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';"
always;
}