Nginx Basics
1. Common Commands
nginx -v # Show version
ps -ef | grep nginx # List nginx processes
nginx # Start nginx
nginx -s reload # Reload configuration
nginx -s stop # Stop nginx
nginx -t # Test configuration for syntax errors
2. Nginx Configuration File
# User/group (not effective on Windows, may cause error if specified)
user www www;
# Number of worker processes, auto adjusts to CPU cores
worker_processes auto;
# Error log
error_log /www/wwwlogs/nginx_error.log crit;
# PID file location
pid /www/server/nginx/logs/nginx.pid;
# Maximum number of open files per worker
worker_rlimit_nofile 51200;
# Stream module for TCP/UDP
stream {
log_format tcp_format '$time_local|$remote_addr|$protocol|$status|$bytes_sent|$bytes_received|$session_time|$upstream_addr|$upstream_bytes_sent|$upstream_bytes_received|$upstream_connect_time';
access_log /www/wwwlogs/tcp-access.log tcp_format;
error_log /www/wwwlogs/tcp-error.log;
include /www/server/panel/vhost/nginx/tcp/*.conf;
}
# Events block
events {
use epoll;
worker_connections 51200;
multi_accept on;
}
# HTTP block
http {
include my_docs.conf;
include mime.types;
# WAF (Web Application Firewall)
#include luawaf.conf;
include proxy.conf;
default_type application/octet-stream;
server_names_hash_bucket_size 512;
client_header_buffer_size 32k;
large_client_header_buffers 4 32k;
client_max_body_size 50m;
sendfile on;
tcp_nopush on;
keepalive_timeout 60;
tcp_nodelay on;
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
fastcgi_buffer_size 64k;
fastcgi_buffers 4 64k;
fastcgi_busy_buffers_size 128k;
fastcgi_temp_file_write_size 256k;
fastcgi_intercept_errors on;
# Gzip compression
gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.1;
gzip_comp_level 2;
gzip_types text/plain application/javascript application/x-javascript text/javascript text/css application/xml;
gzip_vary on;
gzip_proxied expired no-cache no-store private auth;
gzip_disable "MSIE [1-6]\.";
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m;
server_tokens off;
access_log off;
server {
listen 888;
server_name phpmyadmin;
index index.html index.htm index.php;
root /www/server/phpmyadmin;
include enable-php.conf;
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
expires 30d;
}
location ~ .*\.(js|css)?$ {
expires 12h;
}
location ~ /\. {
deny all;
}
access_log /www/wwwlogs/access.log;
}
include /www/server/panel/vhost/nginx/*.conf;
}
3. Reverse Proxy
Forward Proxy: A proxy that acts on behalf of the client, hiding the client's identity from the target server. The client knows the target server, but the target server only knows the proxy.
Reverse Proxy: A proxy that acts on behalf of the server, hiding the server's identity from the client. The client sends requests to the reverse proxy, which forwards them to appropriate backend servers. Commonly used for load balancing.
Key directives:
proxy_pass http://backend_server;
proxy_set_header Header value;
4. History Mode 404 (SPA)
When using HTML5 History mode, the server tries to find a physcial file matching the route, leading to a 404 error. Solution: fallback to index.html.
try_files $uri $uri/ /index.html;
Common variables:
$uri– current request URI (without query string)$args– query string$arg_xxx– specific query parameter (xxxis the parameter name)$http_xxx– request header value$request_uri– full original request URI (with query string)
5. Load Balancing
Basic upstream block:
upstream backend {
server 127.0.0.1:9001;
server 127.0.0.1:9002;
server 127.0.0.1:9003;
}
location / {
proxy_pass http://backend;
}
Weighted distribution:
upstream backend {
server 127.0.0.1:9001 weight=3;
server 127.0.0.1:9002 weight=2;
server 127.0.0.1:9003 weight=1;
}
Other strategies: ip_hash, least_conn, url_hash, fair (response time). Not common used in modern setups due to statelessness.
Parameters:
fail_timeout: time to wait before marking a server as faileddown: do not use the serverbackup: use only when all other servers are down
6. Static vs Dynamic Separation
Static files are served directly by Nginx, dynamic requests go to the backend.
server {
listen 8082;
server_name localhost;
location / {
proxy_pass http://localhost:3022;
}
location ~* \.(js|css|pdf|png|jpg|svg)$ {
root /path/to/static;
}
}
7. URL Rewriting
Rewrite URLs to hide actual backend paths.
location / {
rewrite ^/rewrite\.html$ /api/agg break;
proxy_pass http://localhost:3022;
}
Flags: last (continue matching), break (stop), redirect (302), permanent (301).
8. Gzip Compression
Enable dynamic or static compression.
Dynamic compression (disables sendfile):
gzip on;
gzip_types text/plain text/css application/javascript application/json;
gzip_min_length 256;
gzip_comp_level 5;
Static compression (works with sendfile):
gzip_static on;
gunzip on; # Decompress for clients not supporting gzip
9. Hotlink Protection (Referer)
Prevent external sites from embedding your resources.
location ~* \.(js|css|pdf|png|jpg|svg)$ {
valid_referers 192.168.1.1;
if ($invalid_referer) {
return 403;
}
root /path/to/static;
}
valid_referers options: none, blocked, server_names, or specific IP/domain.
10. Location Matching Rules
| Syntax | Description |
|---|---|
= /path |
Exact match |
/path |
Prefix match |
~ pattern |
Case-sensitive regex |
~* pattern |
Case-insensitive regex |
^~ /path |
Preferential prefix match (higher priority than regex) |
Priority: = > ^~ > ~/~* > plain prefix.
root vs alias:
root: full URI appended to pathalias: only the part after the matched location is appended
Advanced Nginx
Session Persistence
1. ip_hash – (mature, but causes traffic skew and session loss on server failure)
upstream backend {
ip_hash;
server 127.0.0.1:2022;
server 127.0.0.1:3033;
}
2. Custom hash (e.g., $request_uri) – for consistant routing based on URL.
upstream backend {
hash $request_uri;
server 127.0.0.1:2022;
server 127.0.0.1:3033;
}
3. Cookie-based hash – recommended for modern apps.
upstream backend {
hash $cookie_sessionid;
server 127.0.0.1:3044 weight=2 max_fails=2 fail_timeout=30s;
server 127.0.0.1:3033 weight=1 max_fails=2 fail_timeout=30s;
}
Keep-Alive Connections
Client side:
http {
keepalive_timeout 65;
keepalive_time 1h;
send_timeout 60;
keepalive_requests 1000;
}
Upstream side:
upstream backend {
keepalive 100;
keepalive_requests 1000;
server ...;
}
server {
location / {
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_pass http://backend;
}
}
Buffer Settings
client_body_buffer_size: body buffer sizeclient_body_temp_path: temporary file pathclient_max_body_size: max body size (413 if exceeded)client_body_timeout: timeout for receiving body (408 if exceeded)client_header_timeout: timeout for receiving header (408 if exceeded)
Getting Real Client IP
proxy_set_header X-Forwarded-For $remote_addr;
Gzip vs Brotli
Brotli offers better compression. Enable via brotli on; module.
sendfile and Zero-Copy
sendfile enables direct file transfer from disk to network without copying through user space. Dynamic gzip disables this.
SSI (Server Side Includes)
Include common components (header, footer) into HTML.
location / {
ssi on;
root html;
index index.html;
}
Template file example (index.html):
<!--# include file="header.html" -->
<div id="app"></div>
<!--# include file="footer.html" -->
Practical Problem Solving
CORS with Nginx
server {
listen 8088;
server_name 127.0.0.1;
location / {
proxy_pass http://127.0.0.1:5173;
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;
}
location /api {
proxy_pass http://127.0.0.1:3033;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
BFF (Backend for Frontend) Pattern
const express = require('express');
const axios = require('axios');
const path = require('path');
const app = express();
app.use(express.static(path.join(__dirname, '../dist'), {
setHeaders: (res, filePath) => {
if (filePath.endsWith('.gz')) {
res.set('Content-Encoding', 'gzip');
res.set('Content-Type', 'application/gzip');
}
},
}));
app.get('/api/agg', async (req, res) => {
const result = await axios.get('http://127.0.0.1:3033/api/user');
res.send({ msg: 123, code: 200, data: result.data });
});
app.listen(3022);
Load Balancing Example
upstream backend {
server 127.0.0.1:3044 weight=2 max_fails=2 fail_timeout=30s;
server 127.0.0.1:3033 weight=1 max_fails=2 fail_timeout=30s;
}
server {
listen 8088;
server_name 127.0.0.1;
location / {
proxy_pass http://127.0.0.1:5173;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /api {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Canary Release (Gray Release)
Route traffic based on cookies or headers.
upstream v1 {
server 192.168.1.6:3000;
}
upstream v2 {
server 192.168.1.6:3001;
}
upstream default {
server 192.168.1.6:3000;
}
set $group "default";
if ($http_cookie ~* "version=1.0") {
set $group v1;
}
if ($http_cookie ~* "version=2.0") {
set $group v2;
}
location ^~ /api {
rewrite ^/api/(.*)$ /$1 break;
proxy_pass http://$group;
}