Core Directives
user nginx;Worker process userworker_processes auto;Number of worker processes — auto matches CPU countworker_connections 1024;Max simultaneous connections per worker (inside events {})events { worker_connections 1024; }events block — connection handling confighttp { ... }http block — all web serving config lives hereinclude /etc/nginx/conf.d/*.conf;Include all .conf files from a directoryinclude mime.types;Load MIME type mappingsdefault_type application/octet-stream;Fallback MIME type for unknown extensionssendfile on;Use kernel sendfile() for static files — more efficienttcp_nopush on;Send headers in one packet — use with sendfiletcp_nodelay on;Disable Nagle — reduces latency for small packetskeepalive_timeout 65;Seconds to keep idle client connections openclient_max_body_size 10m;Max request body size — 0 disables the limitclient_body_timeout 30;Seconds to wait for client to send request bodysend_timeout 30;Seconds to wait between successive write ops to clienterror_log /var/log/nginx/error.log warn;Error log path and level: debug|info|notice|warn|error|critaccess_log /var/log/nginx/access.log combined;Access log path and format nameaccess_log off;Disable access loggingpid /run/nginx.pid;Path for the master process PID filegzip on;Enable gzip compressiongzip_types text/plain text/css application/json application/javascript;MIME types to compressgzip_min_length 256;Only compress responses larger than N bytesServer Blocks
server { listen 80; server_name example.com; }Basic server blocklisten 80;Listen on port 80 (IPv4)listen [::]:80;Listen on port 80 (IPv6)listen 443 ssl;Listen on port 443 with SSLlisten 80 default_server;Default server — catches unmatched server_name requestsserver_name example.com www.example.com;Match these hostnamesserver_name *.example.com;Wildcard subdomain matchserver_name ~^(?<sub>.+)\.example\.com$;Regex server name with named captureroot /var/www/html;Document root for this serverindex index.html index.php;Default files to serve for directory requestserror_page 404 /404.html;Custom error pageerror_page 500 502 503 504 /50x.html;Multiple error codes to one pagereturn 301 https://$host$request_uri;Redirect HTTP to HTTPSserver_tokens off;Hide Nginx version from error pages and headersLocation Matching
location / { }Prefix match — matches any URI starting with /location = /exact { }= exact match — highest priority, stops searchinglocation ^~ /static/ { }^~ prefix match — stops regex searching if matchedlocation ~ \.php$ { }~ case-sensitive regex matchlocation ~* \.(jpg|png|gif)$ { }~* case-insensitive regex matchlocation @named { }Named location — only for internal redirectstry_files $uri $uri/ /index.html;Try each path in order, serve last as fallbacktry_files $uri $uri/ =404;Return 404 if nothing foundtry_files $uri @fallback;Fall back to named locationalias /var/www/other/;Serve from a different path than rootexpires 30d;Set Cache-Control and Expires headersexpires -1;Disable caching (no-cache)add_header Cache-Control "public, max-age=2592000";Manually set cache headerinternal;Location only accessible via internal redirects, not clientsReverse Proxy
proxy_pass http://127.0.0.1:8080;Forward request to upstream serverproxy_pass http://127.0.0.1:8080/app;Forward with a path prefix addedproxy_pass http://backend;Forward to upstream group named "backend"proxy_http_version 1.1;Use HTTP/1.1 for keep-alive supportproxy_set_header Host $host;Forward original Host headerproxy_set_header X-Real-IP $remote_addr;Forward client IPproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;Append client IP to forwarded-for chainproxy_set_header X-Forwarded-Proto $scheme;Forward http or https schemeproxy_set_header Upgrade $http_upgrade;Required for WebSocket proxyingproxy_set_header Connection "upgrade";Required for WebSocket proxyingproxy_read_timeout 60s;Max time to wait for upstream responseproxy_connect_timeout 10s;Max time to establish connection to upstreamproxy_send_timeout 60s;Max time between successive writes to upstreamproxy_buffering off;Disable buffering — stream response directly to clientproxy_buffer_size 4k;Buffer size for first part of upstream responseproxy_cache_bypass $http_cache_control;Skip cache if header is setproxy_hide_header X-Powered-By;Remove header from upstream before sending to clientSSL / TLS
listen 443 ssl;Enable SSL on port 443ssl_certificate /etc/ssl/certs/cert.pem;Path to certificate (full chain)ssl_certificate_key /etc/ssl/private/key.pem;Path to private keyssl_protocols TLSv1.2 TLSv1.3;Allowed TLS versions — drop TLSv1.0/1.1ssl_ciphers HIGH:!aNULL:!MD5;Allowed cipher suitesssl_prefer_server_ciphers on;Use server cipher preference over clientssl_session_cache shared:SSL:10m;Shared SSL session cache — 10 MB holds ~40k sessionsssl_session_timeout 1d;How long SSL sessions can be reusedssl_session_tickets off;Disable session tickets — improve forward secrecyssl_stapling on; ssl_stapling_verify on;Enable OCSP stapling — faster certificate validationresolver 1.1.1.1 valid=300s;DNS resolver for OCSP staplingadd_header Strict-Transport-Security "max-age=63072000" always;HSTS — force HTTPS for 2 yearsadd_header X-Frame-Options DENY;Prevent clickjackingadd_header X-Content-Type-Options nosniff;Prevent MIME-type sniffingRewrites & Redirects
return 301 https://$host$request_uri;Permanent redirect (301)return 302 /new-path;Temporary redirect (302)return 200 "OK";Return status with bodyreturn 404;Return status with no bodyrewrite ^/old/(.*)$ /new/$1 permanent;Rewrite URL with regex — permanent = 301rewrite ^/old/(.*)$ /new/$1 redirect;Rewrite URL — redirect = 302rewrite ^/old/(.*)$ /new/$1 last;Rewrite then re-search location blocksrewrite ^/old/(.*)$ /new/$1 break;Rewrite then stop processing rewritesif ($host != "example.com") { return 301 https://example.com$request_uri; }Redirect non-canonical domainsif ($request_method = POST) { ... }Condition on request method$uri / $request_uri$uri is decoded and normalised; $request_uri is original$host / $http_host$host is server_name; $http_host includes port if non-standard$scheme"http" or "https"$remote_addr / $remote_portClient IP address / port$args / $arg_nameFull query string / specific query param$request_method / $statusHTTP method (GET, POST...) / response status codeLoad Balancing
upstream backend {
server 10.0.0.1:8080;
server 10.0.0.2:8080;
}Round-robin upstream group (default)least_conn;Route to server with fewest active connectionsip_hash;Route by client IP — sticky sessionshash $request_uri consistent;Route by URI — consistent hashingserver 10.0.0.1:8080 weight=3;Weight — server receives 3x more requestsserver 10.0.0.1:8080 backup;Backup — only used when all primary servers are downserver 10.0.0.1:8080 down;Mark server as permanently unavailableserver 10.0.0.1:8080 max_fails=3 fail_timeout=30s;Passive health check — mark failed after 3 errors in 30skeepalive 32;Keep N idle upstream connections open per workerzone backend 64k;Shared memory zone for upstream state (required for health checks)nginx -tTest configuration for syntax errorsnginx -s reloadReload config without dropping connectionsnginx -s stop / nginx -s quitFast stop / graceful shutdownnginx -VShow version and compiled-in modules