Bỏ qua đến nội dung
DevOps Lab

Nginx — Tài Liệu Toàn Diện

Kiến trúc, Load Balancing, SSL/TLS, Caching và 4 Use Cases thực tế

⚡ DevOps Intern Learning Series

Nginx Toàn Diện

Từ cơ bản đến production-ready — kiến trúc, cấu hình, load balancing, SSL/TLS, caching, và 4 use case thực tế đầy đủ cho DevOps Engineer.

9
Modules chính
6
LB Algorithms
4
Use Cases thực tế
25+
Code examples
1
Giới Thiệu Nginx
Lịch sử, triết lý thiết kế, và vị trí trong hệ sinh thái DevOps

Nginx (đọc là "Engine-X") được Igor Sysoev tạo ra năm 2002, ra mắt công khai năm 2004 nhằm giải quyết bài toán C10K Problem — thách thức xử lý 10,000 kết nối đồng thời mà Apache gặp phải. Ngày nay, Nginx là web server được sử dụng rộng rãi nhất thế giới, chiếm ~34% tổng số website toàn cầu.

🌐
Web Server
Serve static files (HTML, CSS, JS, images) với tốc độ cực cao, I/O hiệu quả nhờ event-driven model.
🔀
Reverse Proxy
Nhận request từ client, chuyển tiếp đến backend servers (Node.js, Python, Java...), che giấu internal topology.
⚖️
Load Balancer
Phân phối traffic đến nhiều upstream servers theo nhiều thuật toán khác nhau.
🔒
SSL Terminator
Giải mã HTTPS tại edge, backend chạy plain HTTP — giảm tải CPU cho app servers.
💾
Cache Server
Cache response từ upstream, giảm latency và tải cho backend đáng kể.
🚪
API Gateway
Rate limiting, authentication, routing cho microservices architectures.

Kiến Trúc Hoạt Động

Nginx sử dụng asynchronous, event-driven architecture — hoàn toàn khác Apache. Hiểu rõ model này là chìa khóa để cấu hình đúng.

🏗 Nginx Process Model & Request Flow
CLIENTS Browser Mobile App API Client Master Process PID 1 — 1 process reload / signals WORKER PROCESSES Worker #1 Event Loop Worker #2 Event Loop Worker #N N = CPU cores Shared Memory (cache) UPSTREAM SERVERS App Server :3000 Node.js / Python / Go App Server :3001 Node.js / Python / Go Database :5432 PostgreSQL / Redis Static Files /var/www/html HTTPS req proxy_pass

So Sánh Nginx vs Apache

⚡ Architecture Comparison — Thread-based vs Event-driven
Apache (Thread-per-connection) 1 request = 1 thread (blocking I/O) Thread 1 Thread 2 Thread 3 Thread N ⚠️ Apache ❌ RAM tăng tuyến tính theo connections Nginx (Event-driven) N requests = 1 worker (non-blocking) Event Loop C1 C2 C3 CN C5 C4 ✅ RAM thấp, xử lý hàng nghìn connections
Tiêu chíNginxApache
Kiến trúcEvent-driven, asyncThread/process-based
RAM usage (10k conn)~150MB~1.5GB+
Static file performanceRất cao ⚡Trung bình
Dynamic contentCần proxy passmod_php native
.htaccessKhông hỗ trợHỗ trợ đầy đủ
Config reloadZero-downtimeGraceful restart
ModulesCompile-timeDynamic (DSO)
Use case tốt nhấtHigh-traffic, microservicesShared hosting, PHP legacy
2
Các Khái Niệm Cơ Bản
Worker process, event loop, và cấu trúc nginx.conf

Worker Process Model

Nginx có 1 master process và nhiều worker processes. Master process quản lý config và signal; workers xử lý actual connections.

💡
Best Practice
Set worker_processes auto; để Nginx tự detect số CPU cores và spawn đúng số workers. Mỗi worker chạy 1 event loop, xử lý hàng nghìn connections đồng thời mà không block.

Cấu Trúc nginx.conf

File config của Nginx được tổ chức theo dạng blocks lồng nhau: main → events → http → server → location.

NGINX /etc/nginx/nginx.conf
# ═══════════════════════════════════════════════════
# MAIN CONTEXT — Global settings, chạy trước tất cả
# ═══════════════════════════════════════════════════
user  nginx;                        # User chạy worker processes
worker_processes  auto;             # 1 worker per CPU core (recommended)
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;       # PID file của master process

# ═══════════════════════════════════════════════════
# EVENTS CONTEXT — Cấu hình connection handling
# ═══════════════════════════════════════════════════
events {
    worker_connections  1024;        # Max connections per worker
    # Total max connections = worker_processes × worker_connections
    use  epoll;                       # Linux: epoll (best), macOS: kqueue
    multi_accept  on;                # Accept nhiều connections cùng lúc
}

# ═══════════════════════════════════════════════════
# HTTP CONTEXT — Web server settings
# ═══════════════════════════════════════════════════
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    # Access log format
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile    on;      # Dùng sendfile() syscall — hiệu quả hơn read/write
    tcp_nopush  on;      # Gửi headers và data cùng một TCP packet
    tcp_nodelay on;      # Disable Nagle algorithm cho WebSockets

    keepalive_timeout  65;   # Keep connection alive 65 giây
    server_tokens      off;  # Ẩn version Nginx khỏi response headers

    # ─────────────────────────────────────────────────
    # SERVER CONTEXT — Virtual host / site config
    # ─────────────────────────────────────────────────
    server {
        listen       80;
        server_name  example.com www.example.com;
        root         /var/www/html;

        # ─────────────────────────────────────
        # LOCATION CONTEXT — URL pattern matching
        # ─────────────────────────────────────
        location / {
            try_files $uri $uri/ =404;
        }

        location /api/ {
            proxy_pass http://backend:3000;
        }
    }

    # Load thêm config từ thư mục conf.d
    include /etc/nginx/conf.d/*.conf;
}

Location Block — Matching Priority

Nginx match location theo thứ tự ưu tiên từ cao đến thấp:

NGINXlocation matching
# 1. EXACT MATCH (=) — Ưu tiên cao nhất
location = /favicon.ico { return 204; }

# 2. PREFERENTIAL PREFIX (^~) — Tìm thấy thì dừng, không thử regex
location ^~ /static/ { root /var/www; }

# 3. CASE-SENSITIVE REGEX (~) — Theo thứ tự trong file
location ~ .php$ { fastcgi_pass php-fpm:9000; }

# 4. CASE-INSENSITIVE REGEX (~*)
location ~* .(jpg|jpeg|png|gif)$ {
    expires 30d;
}

# 5. PREFIX MATCH (không có modifier) — Ưu tiên thấp nhất
location /api/ { proxy_pass http://api-backend; }
3
Load Balancing
Các thuật toán và cấu hình phân phối traffic
⚖️ Load Balancing Topology
🌍 Internet ~10k req/s Nginx Load Balancer :80 / :443 health check ✓ App Server 1 192.168.1.10:3000 App Server 2 192.168.1.11:3000 App Server 3 192.168.1.12:3000 DOWN ✗ auto-removed w=3 w=2 w=1 w = weight

Các Thuật Toán Load Balancing

Nginx hỗ trợ 6 thuật toán chính — mỗi cái phù hợp với một kiểu workload khác nhau. Hiểu rõ từng thuật toán giúp bạn chọn đúng ngay từ đầu, tránh phải debug sau khi production bị overload.

🎯
Default Algorithm
Round Robin là thuật toán mặc định của Nginx — không cần khai báo gì trong upstream block. Nginx sẽ tự động phân phối request lần lượt đến từng server theo thứ tự. Đây là lựa chọn tốt cho hầu hết trường hợp khi các server có cùng cấu hình.
ALGO 1
🔄 Round Robin DEFAULT
Phân phối request theo vòng tròn — server 1, 2, 3, rồi lại 1, 2, 3…
Khai báo
(không cần gì) — mặc định
Phù hợp
Request đồng đều, server đồng nhất cấu hình, stateless apps
Không phù hợp
Server có hiệu năng khác nhau; request có thời gian xử lý chênh lệch lớn
🔄 Round Robin — Phân phối tuần tự
Thứ tự request đến: R1 → R2 → R3 → R4 → R5 → R6 → R7 → R8 → R9 Nginx round robin (default) Server A :3001 Server B :3002 Server C :3003 Thứ tự phân phối REQUEST → SERVER CYCLE R1, R4, R7 → Server A #1 R2, R5, R8 → Server B #2 R3, R6, R9 → Server C #3 ✓ Mỗi server nhận đúng 33% traffic Lý tưởng khi tất cả server giống nhau
NGINXround-robin (default)
upstream backend {
    # Không cần khai báo gì — Round Robin là default
    server 10.0.0.1:3000;   # nhận R1, R4, R7...
    server 10.0.0.2:3000;   # nhận R2, R5, R8...
    server 10.0.0.3:3000;   # nhận R3, R6, R9...
}
ALGO 2
⚖️ Weighted Round Robin
Round Robin có trọng số — server mạnh hơn nhận nhiều request hơn theo tỷ lệ
Khai báo
weight=N trên từng server
Phù hợp
Server có cấu hình phần cứng khác nhau (4 core vs 16 core); phân bổ theo capacity
Ví dụ
Server mới có RAM gấp đôi → weight=2 để nhận gấp đôi traffic
⚖️ Weighted Round Robin — Phân phối theo tỷ lệ weight
Nginx weighted round robin Server A weight=3 → 50% Server B weight=2 → 33% Server C weight=1 → 17% Với 6 requests liên tiếp (total weight = 6) A A A (3 slots) B B (2) C Server A: 3 / 6 = 50% traffic 16-core Server B: 2 / 6 = 33% traffic 8-core Server C: 1 / 6 = 17% traffic 4-core Tỷ lệ = weight / tổng weight = 3/6, 2/6, 1/6
NGINXweighted round robin
upstream backend {
    # weight mặc định = 1 nếu không khai báo
    # Tổng weight = 6 → tỷ lệ phân phối tương ứng
    server 10.0.0.1:3000 weight=3;  # 3/6 = 50% — server mạnh nhất
    server 10.0.0.2:3000 weight=2;  # 2/6 = 33%
    server 10.0.0.3:3000 weight=1;  # 1/6 = 17% — server yếu nhất
}
ALGO 3
📊 Least Connections (least_conn)
Gửi request tới server đang có ít active connections nhất tại thời điểm đó
Khai báo
least_conn; trong upstream block
Phù hợp
Workload có thời gian xử lý không đều nhau (mix quick/slow requests)
Không phù hợp
Stateless short-lived requests — overhead tracking không mang lại lợi ích
📊 Least Connections — Ưu tiên server ít tải nhất
t=0 t=5s (slow req arrives) t=10s (new request) Server A conn: 2 Server B conn: 2 Server C conn: 8 ⚠️ Initial state slow req to A Server A conn: 6 🔴 Server B conn: 1 🟢 Server C conn: 9 🔴 A đang bận Nginx decides: A=6, B=1, C=9 → Send to B! Server A conn: 6 skip Server B ✓ CHOSEN Server C conn: 9 skip New req → B Key Insight Round Robin sẽ gửi tiếp vào A (đang bận) least_conn tránh được!
NGINXleast_conn
upstream backend {
    least_conn;  # Bắt buộc khai báo tường minh

    server 10.0.0.1:3000;
    server 10.0.0.2:3000;
    server 10.0.0.3:3000;

    # Kết hợp với weight: server mạnh có "capacity" cao hơn
    # server 10.0.0.1:3000 weight=2;  ← Nginx cân nhắc
    # → server cần có weight×2 connections để bằng server weight=1
}
ALGO 4
📌 IP Hash (ip_hash)
Sticky sessions — cùng IP luôn được route đến cùng một server
Khai báo
ip_hash; trong upstream block
Phù hợp
App có session state lưu trên server (không dùng Redis/shared session); shopping cart in-memory
Lưu ý quan trọng
Có thể gây mất cân bằng nếu nhiều user ở sau cùng một NAT IP; không dùng nếu có Redis session
📌 IP Hash — Sticky Session: cùng IP → cùng Server
User A IP: 1.2.3.4 User B IP: 5.6.7.8 User C IP: 9.10.11.12 Nginx ip_hash hash(IP) % N N = server count consistent mapping Server 1 session: UserA 🔒 session: UserC 🔒 Server 2 session: UserB 🔒 Server 3 (no sessions) A→S1 B→S2 C→S1 ⚠️ Limitation Corporate NAT: 1000 users cùng IP → 1 server quá tải ✓ Best practice: dùng Redis session thay thế
NGINXip_hash
upstream backend {
    ip_hash;   # hash dựa trên 3 octet đầu của IPv4: x.x.x.*

    server 10.0.0.1:3000;
    server 10.0.0.2:3000;
    server 10.0.0.3:3000;

    # Khi cần tạm thời remove server mà không reset sessions:
    server 10.0.0.4:3000 down;  # 'down' thay vì xóa — giữ hash mapping
}
ALGO 5 & 6
🔑 Generic Hash   +   🎲 Random
Hash theo key tùy chỉnh (URL, cookie, header) và phân phối ngẫu nhiên
🔑 Generic Hash
Hash theo bất kỳ variable nào: $uri, $request_uri, $cookie_sessionid, $arg_id...

Use case: Cache locality — cùng URL luôn về cùng 1 server → cache hit rate cao hơn. Thường dùng với Varnish hoặc Memcached backend.
🎲 Random (Nginx Plus)
Chọn ngẫu nhiên, có thể kết hợp two least_conn — chọn 2 server ngẫu nhiên rồi lấy server ít connection hơn.

Use case: Distributed environments với nhiều Nginx instances để tránh coordination overhead.
NGINXhash + random
# ─── Generic Hash — hash theo URI ───────────────────
upstream cache_backends {
    hash $request_uri consistent;
    # 'consistent' = ketama consistent hashing
    # → thêm/bớt server chỉ rehash ~1/N keys thay vì toàn bộ
    server cache1:11211;
    server cache2:11211;
    server cache3:11211;
}

# ─── Hash theo cookie sessionid ─────────────────────
upstream session_backends {
    hash $cookie_sessionid;  # sticky theo session cookie
    server 10.0.0.1:3000;
    server 10.0.0.2:3000;
}

# ─── Random (Nginx Plus / community module) ─────────
# upstream random_backends {
#     random two least_conn;  # 2 random → pick least conn
#     server 10.0.0.1:3000;
#     server 10.0.0.2:3000;
# }

So Sánh Tổng Thể & Hướng Dẫn Chọn Thuật Toán

🗺 Decision Tree — Chọn thuật toán nào?
Cần Load Balance? Servers có spec giống nhau? YES NO Cần sticky sessions? Dùng weight theo capacity YES ip_hash hoặc hash $cookie NO Request duration đồng đều? YES Round Robin ⭐ Default — no config NO least_conn Mixed req duration Weighted RR weight=N per server Generic Hash Cache backends * Random (Nginx Plus): dùng cho distributed multi-LB environments * least_conn + weight: Nginx cân nhắc "effective connections" = connections / weight
Thuật Toán Khai Báo Open Source Ưu Điểm Nhược Điểm Best For
Round Robin DEFAULT (không cần) ✓ OSS Đơn giản, phân phối đều Không quan tâm server load Stateless APIs, microservices đồng nhất
Weighted RR weight=N ✓ OSS Tận dụng server mạnh hơn Vẫn không theo real-time load Mixed hardware, gradual rollout (canary)
Least Connections least_conn; ✓ OSS Adaptive, tránh overload server Overhead tracking; kém hiệu quả với short requests Long-running connections, WebSockets, streaming
IP Hash ip_hash; ✓ OSS Sticky sessions, đơn giản Mất cân bằng với NAT; server down mất session Legacy apps không có shared session
Generic Hash hash $key [consistent]; ✓ OSS Flexible, cache locality tốt Có thể mất cân bằng theo key distribution Cache backends (Memcached, Varnish)
Random random [two]; Nginx Plus Tốt cho multi-LB environments Cần Nginx Plus; ít predictable Distributed deployments với nhiều LB nodes
💡
Pro Tip — Canary Deployment với Weighted Round Robin
Bạn có thể dùng weight để triển khai Canary Release — đưa version mới cho 10% traffic trước:

server app-v1:3000 weight=9;  # 90% — stable version
server app-v2:3000 weight=1;  # 10% — canary version

Nếu v2 OK → tăng weight dần. Nếu v2 lỗi → set weight=0 hoặc thêm down.
NGINX/etc/nginx/conf.d/load-balancer.conf
# ═══════════════════════════════════════════════════
# UPSTREAM — Định nghĩa pool của backend servers
# ═══════════════════════════════════════════════════
upstream app_backend {
    # Load balancing algorithm (chọn 1):

    # 1. Round Robin (default) — lần lượt
    # (không cần khai báo gì thêm)

    # 2. Least Connections — ưu tiên server ít connection nhất
    # least_conn;

    # 3. IP Hash — sticky sessions theo client IP
    # ip_hash;

    # 4. Weighted Round Robin — server mạnh hơn nhận nhiều hơn
    server 192.168.1.10:3000 weight=3;  # 60% traffic
    server 192.168.1.11:3000 weight=2;  # 40% traffic
    server 192.168.1.12:3000 weight=1;  # 20% traffic

    # Backup server — chỉ dùng khi tất cả primary down
    server 192.168.1.20:3000 backup;

    # Health check parameters
    # max_fails: số lần fail trước khi mark down
    # fail_timeout: thời gian mark down
    server 192.168.1.13:3000 max_fails=3 fail_timeout=30s;

    # Keepalive connections tới upstream
    keepalive 32;  # Max 32 idle connections per worker
}

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass         http://app_backend;
        proxy_http_version 1.1;

        # Headers quan trọng cho upstream
        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;

        # Timeout settings
        proxy_connect_timeout  5s;   # Timeout kết nối tới upstream
        proxy_send_timeout     60s;  # Timeout gửi request lên upstream
        proxy_read_timeout     60s;  # Timeout nhận response từ upstream

        # Buffer settings
        proxy_buffer_size       4k;
        proxy_buffers           8 4k;
        proxy_busy_buffers_size 8k;
    }
}
4
SSL/TLS Configuration
HTTPS, certificate management, và SSL termination
🔒 SSL Termination Flow
Browser HTTPS :443 🔐 Encrypted TLS 1.3 Nginx SSL Terminator Decrypt here cert.pem + key.pem 📦 Plaintext HTTP :3000 Backend Apps Không cần xử lý TLS ✓ Giảm CPU load ✓ Centralized cert mgmt Let's Encrypt / CA cert
NGINXssl-configuration.conf
# ═══════════════════════════════════════════════════
# HTTP → HTTPS redirect
# ═══════════════════════════════════════════════════
server {
    listen 80;
    server_name example.com www.example.com;
    # Redirect tất cả HTTP sang HTTPS với 301
    return 301 https://$server_name$request_uri;
}

# ═══════════════════════════════════════════════════
# HTTPS Server Block
# ═══════════════════════════════════════════════════
server {
    listen 443 ssl http2;          # HTTP/2 cần ssl
    server_name example.com;

    # ─────────────── Certificate ───────────────
    ssl_certificate      /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key  /etc/letsencrypt/live/example.com/privkey.pem;

    # ─────────────── Protocol & Ciphers ────────
    ssl_protocols        TLSv1.2 TLSv1.3;   # Chỉ dùng TLS 1.2+ (tắt TLS 1.0/1.1)
    ssl_ciphers          ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
    ssl_prefer_server_ciphers on;           # Server chọn cipher thay vì client
    ssl_ecdh_curve       secp384r1;

    # ─────────────── Session Cache ─────────────
    ssl_session_cache    shared:SSL:10m;     # Share session cache giữa workers
    ssl_session_timeout  10m;               # Resume session trong 10 phút
    ssl_session_tickets  off;               # Tắt tickets vì PFS concerns

    # ─────────────── OCSP Stapling ─────────────
    ssl_stapling         on;                # Server tự fetch OCSP response
    ssl_stapling_verify  on;
    resolver             8.8.8.8 1.1.1.1 valid=300s;

    # ─────────────── Security Headers ──────────
    add_header Strict-Transport-Security  "max-age=63072000; includeSubDomains; preload" always;
    add_header X-Frame-Options            SAMEORIGIN;
    add_header X-Content-Type-Options     nosniff;
    add_header X-XSS-Protection           "1; mode=block";
    add_header Referrer-Policy            "no-referrer-when-downgrade";

    location / {
        proxy_pass http://app_backend;
        include    /etc/nginx/proxy_params;
    }
}
5
Caching & Compression
Tối ưu bandwidth và giảm tải backend
NGINXcaching-compression.conf
# ═══════════════════════════════════════════════════
# PROXY CACHE — Cache response từ upstream
# ═══════════════════════════════════════════════════
http {
    # Khai báo cache zone (trong http context)
    # keys_zone=name:size — shared memory cho cache keys
    # max_size — tổng disk space cho cache
    # inactive — xóa entries không được access trong 60p
    proxy_cache_path  /var/cache/nginx
                      levels=1:2
                      keys_zone=api_cache:10m
                      max_size=1g
                      inactive=60m
                      use_temp_path=off;

    # ═══════════════════════════════════════════════════
    # GZIP COMPRESSION
    # ═══════════════════════════════════════════════════
    gzip              on;
    gzip_vary         on;          # Thêm Vary: Accept-Encoding header
    gzip_proxied      any;         # Compress cả proxied responses
    gzip_comp_level   6;            # 1-9, level 6 = tốt nhất cost/benefit
    gzip_min_length   1024;         # Không compress file nhỏ hơn 1KB
    gzip_types
        text/plain text/css text/xml
        application/json application/javascript
        application/xml+rss image/svg+xml;

    server {
        listen 80;

        # ─────────────── Proxy Cache Usage ─────────
        location /api/ {
            proxy_pass         http://api_backend;
            proxy_cache        api_cache;          # Dùng zone đã khai báo
            proxy_cache_valid  200 302 5m;        # Cache 200/302 trong 5 phút
            proxy_cache_valid  404     1m;        # Cache 404 trong 1 phút
            proxy_cache_use_stale error timeout updating; # Serve stale nếu backend lỗi
            proxy_cache_lock   on;                # Chỉ 1 request đến backend khi cache miss

            # Bypass cache theo header (cho testing)
            proxy_cache_bypass $http_cache_bypass;
            proxy_no_cache     $http_cache_bypass;

            # Thêm header để debug cache HIT/MISS/BYPASS
            add_header X-Cache-Status $upstream_cache_status;
        }

        # ─────────────── Browser Cache cho Static Files ─
        location ~* .(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
            root      /var/www/html;
            expires   1y;                   # Cache 1 năm trên browser
            add_header Cache-Control "public, immutable";
            access_log off;               # Tắt log cho static files
        }
    }
}
6
URL Rewriting & Redirects
rewrite, return, try_files — khi nào dùng cái nào
NGINXurl-rewriting.conf
server {
    listen 80;

    # ─────────────── RETURN (nhanh nhất, không process regex) ────
    # Dùng cho redirect đơn giản, không cần logic
    return 301 https://$host$request_uri;       # HTTP → HTTPS
    # return 302 /new-path$request_uri;          # Temporary redirect
    # return 200 "OK";                           # Trả về text
    # return 204;                                # No content (favicon)

    # ─────────────── REWRITE (có thể dùng regex) ─────────────────
    # Syntax: rewrite regex replacement [flag]
    # Flags: last, break, redirect(302), permanent(301)
    rewrite ^/old-blog/(.*)$   /blog/$1   permanent;   # 301
    rewrite ^/user/(d+)$      /profile?id=$1   last;

    # ─────────────── TRY_FILES ───────────────────────────────────
    # Thử từng file, fallback cuối cùng là named location hoặc code
    location / {
        # Thử: exact file → directory index → 404
        try_files $uri $uri/ =404;
    }

    location /spa/ {
        # Single Page App: fallback về index.html (React/Vue router)
        try_files $uri $uri/ /spa/index.html;
    }

    location /api/ {
        # Thử file static trước, nếu không có thì proxy lên backend
        try_files $uri @api_backend;
    }

    location @api_backend {
        proxy_pass http://backend:3000;
    }

    # ─────────────── MAP — Dynamic variable ──────────────────────
    # Dùng trong http{} context để mapping variables
}

Map Directive — Dynamic Logic

NGINX
http {
    # Map để routing dựa theo subdomain
    map $host $backend {
        default          backend_prod;
        ~^api.          backend_api;
        ~^staging.      backend_staging;
        ~^admin.        backend_admin;
    }

    # Map để limit rate theo loại IP
    map $remote_addr $limit_key {
        default          $binary_remote_addr;
        10.0.0.0/8       "";  # Internal IPs không bị rate limit
        172.16.0.0/12    "";
    }

    limit_req_zone $limit_key zone=api_limit:10m rate=10r/s;

    server {
        location /api/ {
            limit_req zone=api_limit burst=20 nodelay;
            proxy_pass http://$backend;
        }
    }
}
7
Performance Tuning
Tối ưu cho production workloads
⚙️ OS-level Tuning
  • worker_processes auto — match CPU cores
  • worker_rlimit_nofile — tăng file descriptor limit
  • use epoll — Linux event mechanism tốt nhất
  • multi_accept on — nhận nhiều conn cùng lúc
🌐 HTTP-level Tuning
  • sendfile on — zero-copy file transfer
  • tcp_nopush on — batch TCP packets
  • keepalive_timeout — reuse connections
  • gzip — compress text responses
NGINXperformance-tuned.conf
# SYSTEM LIMITS — /etc/security/limits.conf
# nginx soft nofile 65535
# nginx hard nofile 65535

worker_processes  auto;           # = số CPU cores
worker_rlimit_nofile 65535;       # Max file descriptors per worker

events {
    worker_connections 4096;       # Max conn/worker. Total = workers × 4096
    use epoll;
    multi_accept on;
}

http {
    # TCP optimization
    sendfile        on;
    tcp_nopush      on;
    tcp_nodelay     on;

    # Keepalive
    keepalive_timeout         30;      # 30s idle timeout
    keepalive_requests        1000;    # Max requests per connection
    reset_timedout_connection on;      # Free up memory cho timed-out conn

    # Buffer tuning
    client_body_buffer_size    128k;
    client_max_body_size       50m;    # Max upload size
    client_header_buffer_size  1k;
    large_client_header_buffers 4 8k;
    client_body_timeout        10;
    client_header_timeout      10;
    send_timeout               10;

    # Open file cache
    open_file_cache          max=1000 inactive=20s;
    open_file_cache_valid    30s;
    open_file_cache_min_uses 2;
    open_file_cache_errors   on;

    # Hide Nginx version
    server_tokens off;
}
UC
Use Cases Thực Tế
4 kịch bản production với full configuration
USE CASE 1
🔀 Reverse Proxy cho Microservices
Một public endpoint Nginx routing traffic đến nhiều microservices backend
Context
E-commerce platform với 5 microservices độc lập: auth, product, order, payment, notification
Challenge
Client không nên biết internal topology; mỗi service chạy port khác nhau; cần SSL termination tập trung
Solution
Nginx làm single entry point, route theo URL prefix, add security headers, handle SSL
🏗 Microservices Routing Diagram
Client api.shop.com HTTPS :443 Nginx Gateway /auth/* /products/* /orders/* /payment/* /notify/* Auth :4001 Product :4002 Order :4003 Payment :4004 Notify :4005 Docker Network 172.20.0.0/16 Internal communication ✓ Not exposed to internet ✓ Service discovery by name ✓ Nginx only public port
NGINXmicroservices-gateway.conf
# ═══════════════════════════════════════════════════
# Reverse Proxy — Microservices Gateway
# Context: Docker Compose, services communicate by name
# ═══════════════════════════════════════════════════

# Định nghĩa upstream cho từng service
upstream auth_service    { server auth:4001;    keepalive 16; }
upstream product_service { server product:4002; keepalive 16; }
upstream order_service   { server order:4003;   keepalive 16; }
upstream payment_service { server payment:4004; keepalive 8;  }
upstream notify_service  { server notify:4005;  keepalive 8;  }

# Reusable proxy config snippet
# (thường đặt trong /etc/nginx/proxy_params)
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 443 ssl http2;
    server_name api.shop.com;

    ssl_certificate      /etc/ssl/certs/shop.pem;
    ssl_certificate_key  /etc/ssl/private/shop.key;

    # Common proxy headers — inject vào mọi request
    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_http_version                 1.1;
    proxy_set_header Connection        "";

    # ─── Auth Service ─────────────────────────────────
    location /auth/ {
        proxy_pass http://auth_service/;   # trailing / strips prefix
        proxy_read_timeout 30s;
    }

    # ─── Product Service ──────────────────────────────
    location /products/ {
        proxy_pass http://product_service/;
        proxy_cache api_cache;
        proxy_cache_valid 200 2m;
        add_header X-Cache-Status $upstream_cache_status;
    }

    # ─── Order Service ────────────────────────────────
    location /orders/ {
        proxy_pass http://order_service/;
        # Rate limit để chống spam
        limit_req zone=api_limit burst=10 nodelay;
    }

    # ─── Payment Service — Extra security ─────────────
    location /payment/ {
        proxy_pass http://payment_service/;
        # Chỉ cho phép từ authenticated users (verify upstream)
        proxy_read_timeout 60s;
        # Không cache payment responses
        add_header Cache-Control "no-store, no-cache";
    }

    # ─── WebSocket for Notifications ──────────────────
    location /ws/notify/ {
        proxy_pass http://notify_service/;
        proxy_set_header Upgrade    $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_read_timeout 3600s;   # WebSocket cần timeout dài
    }

    # Health check endpoint cho load balancer bên ngoài
    location /health {
        return 200 '{"status":"ok"}' ;
        add_header Content-Type application/json;
        access_log off;
    }
}
USE CASE 2
⚡ Load Balancer cho High-Traffic Website
Multi-tier load balancing với health checks và session persistence
Context
News website với 500k pageviews/ngày, traffic spike vào buổi sáng. 5 app servers, 2 tiers
Challenge
Zero downtime khi deploy, tự động remove server bị down, session không bị mất khi switching
Solution
Nginx LB với least_conn, passive health checks, upstream keepalive, và graceful failover
NGINXhigh-traffic-lb.conf
# ═══════════════════════════════════════════════════
# Load Balancer — High Traffic News Site
# Architecture: Internet → CDN → Nginx LB → App Servers
# ═══════════════════════════════════════════════════

upstream news_app {
    least_conn;   # Route tới server có ít active connections nhất

    # Primary servers với health check params
    server app1.internal:8080 weight=5 max_fails=3 fail_timeout=30s;
    server app2.internal:8080 weight=5 max_fails=3 fail_timeout=30s;
    server app3.internal:8080 weight=3 max_fails=3 fail_timeout=30s;
    server app4.internal:8080 weight=3 max_fails=3 fail_timeout=30s;
    server app5.internal:8080 weight=1 max_fails=3 fail_timeout=30s;

    # Backup — chỉ dùng khi tất cả primary fail
    server backup.internal:8080 backup;

    # Keepalive 64 connections tới upstream per worker
    keepalive 64;
    keepalive_requests 100;
    keepalive_timeout  60s;
}

# Separate upstream cho static assets
upstream static_servers {
    server static1.internal:9090;
    server static2.internal:9090;
    keepalive 32;
}

server {
    listen      443 ssl http2;
    server_name news.example.com;

    ssl_certificate      /etc/ssl/certs/news.crt;
    ssl_certificate_key  /etc/ssl/private/news.key;

    # Gzip cho HTML/JS/CSS
    gzip on;
    gzip_types text/html text/css application/javascript application/json;
    gzip_comp_level 4;

    # ─── Dynamic content → App servers ────────────────
    location / {
        proxy_pass          http://news_app;
        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;

        # Retry logic: nếu server lỗi thì thử server khác
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
        proxy_next_upstream_tries 3;    # Thử tối đa 3 servers
        proxy_next_upstream_timeout 10s;
    }

    # ─── Static assets → dedicated servers ───────────
    location ~* .(css|js|png|jpg|woff2|svg)$ {
        proxy_pass  http://static_servers;
        expires     30d;
        add_header  Cache-Control "public, immutable";
        add_header  Vary          "Accept-Encoding";
        access_log  off;
    }

    # ─── Maintenance page (khi deploy) ───────────────
    # Uncomment khi cần maintenance:
    # error_page 502 503 /maintenance.html;
    # location = /maintenance.html {
    #     root /var/www/maintenance;
    #     internal;
    # }
}
USE CASE 3
📦 Static File Serving với Caching
High-performance static site với aggressive caching strategy
Context
React SPA build artifacts + file downloads. Files thay đổi ít, cần cache tối đa
Challenge
HTML phải revalidate (nội dung thay đổi), assets có content hash nên cache vĩnh viễn
Solution
Phân biệt cache policy theo loại file, ETag, brotli/gzip compression
NGINXstatic-file-server.conf
server {
    listen      443 ssl http2;
    server_name cdn.example.com;

    root        /var/www/dist;    # React build output
    index       index.html;

    # ─── Compression ────────────────────────────────
    gzip            on;
    gzip_static     on;   # Serve pre-compressed .gz files nếu có
    gzip_types      text/html text/css application/javascript
                    application/json image/svg+xml;
    gzip_comp_level 9;   # Level cao nhất cho static files

    # ─── HTML files — NO cache (revalidate every time) ─
    location ~* .html$ {
        add_header Cache-Control "no-cache, must-revalidate";
        add_header ETag         $uri;
        expires    0;
        try_files  $uri /index.html;  # SPA fallback
    }

    # ─── Hashed assets — Cache 1 year ────────────────
    # Vite/CRA tạo tên file có hash: main.abc123.js
    location ~* .[0-9a-f]{8}.(css|js)$ {
        add_header Cache-Control "public, max-age=31536000, immutable";
        expires    1y;
        access_log off;
    }

    # ─── Images — Cache 30 days ──────────────────────
    location ~* .(png|jpg|jpeg|gif|webp|avif|ico|svg)$ {
        add_header Cache-Control "public, max-age=2592000";
        expires    30d;
        access_log off;
    }

    # ─── Fonts — Cache 1 year, CORS needed ───────────
    location ~* .(woff|woff2|ttf|otf|eot)$ {
        add_header Cache-Control        "public, max-age=31536000, immutable";
        add_header Access-Control-Allow-Origin "*";  # Required cho cross-origin fonts
        expires    1y;
    }

    # ─── Downloads — No cache, force download ─────────
    location /downloads/ {
        alias          /var/files/downloads/;
        add_header     Content-Disposition "attachment";
        add_header     Cache-Control       "no-store";
        # Limit bandwidth per connection
        limit_rate     5m;   # 5MB/s per connection
        limit_rate_after 10m; # Bắt đầu limit sau 10MB đầu
    }

    # ─── SPA catch-all ───────────────────────────────
    location / {
        try_files $uri $uri/ /index.html;
    }

    # Security — block dot files và hidden
    location ~ /. {
        deny all;
        return 404;
    }
}
USE CASE 4
🚪 API Gateway Configuration
Rate limiting, authentication passthrough, và request/response manipulation
Context
Public API với free/premium tiers, cần rate limit theo tier, logging chi tiết, CORS handling
Challenge
Rate limit khác nhau theo API key tier; cần strip/add headers; handle OPTIONS preflight
Solution
limit_req_zone với multiple zones, map directive, sub_filter để transform responses
NGINXapi-gateway.conf
http {
    # ─── Rate Limit Zones ────────────────────────────
    # Zone cho anonymous users: 10 req/s
    limit_req_zone  $binary_remote_addr
                    zone=anon_limit:10m
                    rate=10r/s;

    # Zone cho authenticated users: 100 req/s
    limit_req_zone  $binary_remote_addr
                    zone=auth_limit:20m
                    rate=100r/s;

    # Connection limit per IP
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;

    # Map: detect authenticated request by header
    map $http_x_api_key $is_authenticated {
        default        0;
        ~^[A-Za-z0-9]{32}$  1;  # Nếu API key đúng format
    }

    # Detailed log format cho API access
    log_format  api_log
        '$time_iso8601 $remote_addr "$request" $status '
        '$body_bytes_sent ${request_time}s '
        '"$http_x_api_key" "$http_user_agent"';

    server {
        listen      443 ssl http2;
        server_name api.example.com;

        access_log  /var/log/nginx/api_access.log  api_log;

        # ─── CORS Headers ────────────────────────────
        map $http_origin $cors_origin {
            default                   "";
            ~^https://app.example.com$  $http_origin;
            ~^https://.*.trusted.io$    $http_origin;
        }

        location /v1/ {
            # CORS preflight handling
            if ($request_method = OPTIONS) {
                add_header Access-Control-Allow-Origin  $cors_origin;
                add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
                add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-API-Key";
                add_header Access-Control-Max-Age        86400;
                return 204;
            }

            add_header Access-Control-Allow-Origin $cors_origin always;

            # Apply rate limit theo tier
            if ($is_authenticated = 1) {
                # set $rate_zone auth_limit;  ← không thể dùng if cho limit_req
                # Workaround: dùng 2 location blocks
            }

            limit_req       zone=anon_limit burst=20;
            limit_req_status 429;   # Trả 429 Too Many Requests
            limit_conn       conn_limit 20;

            # Strip internal headers trước khi forward
            proxy_set_header  X-Internal-Token  "";  # Remove để security
            proxy_set_header  X-API-Key         $http_x_api_key;
            proxy_set_header  X-Real-IP         $remote_addr;
            proxy_set_header  X-Request-ID      $request_id;  # Unique request ID

            proxy_pass  http://api_backend;

            # Add response headers
            add_header  X-Request-ID     $request_id   always;
            add_header  X-Rate-Limit-Limit "10"         always;
        }

        # Authenticated endpoint — higher rate limit
        location /v1/premium/ {
            limit_req  zone=auth_limit burst=200 nodelay;
            proxy_pass http://api_backend;
        }

        # API docs — public, no rate limit
        location /docs/ {
            proxy_pass http://docs_server;
            expires    1h;
        }

        # 429 custom error page
        error_page 429 /rate_limit.json;
        location = /rate_limit.json {
            return 429 '{"error":"rate_limit_exceeded","retry_after":60}';
            add_header Content-Type    application/json;
            add_header Retry-After     60;
        }
    }
}
8
Cài Đặt & Commands
Setup, lifecycle management, và daily operations

Cài Đặt Nginx

BASHinstall.sh
# ─── Ubuntu / Debian ─────────────────────────────────
sudo apt update
sudo apt install -y nginx

# Dùng official Nginx repo để có version mới nhất
curl -fsSL https://nginx.org/keys/nginx_signing.key | sudo gpg --dearmor -o /usr/share/keyrings/nginx-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/nginx-keyring.gpg] http://nginx.org/packages/ubuntu $(lsb_release -cs) nginx"   | sudo tee /etc/apt/sources.list.d/nginx.list
sudo apt update && sudo apt install -y nginx

# ─── CentOS / RHEL / Amazon Linux ───────────────────
sudo yum install -y epel-release
sudo yum install -y nginx

# ─── Docker ─────────────────────────────────────────
docker run -d   --name nginx   -p 80:80 -p 443:443   -v /path/to/nginx.conf:/etc/nginx/nginx.conf:ro   -v /path/to/certs:/etc/ssl/certs:ro   nginx:alpine

# Kiểm tra version và modules
nginx -V 2>&1 | head -5

Lifecycle Commands

sudo systemctl start nginx
Khởi động Nginx
sudo systemctl stop nginx
Dừng Nginx
sudo systemctl restart nginx
Restart (có downtime)
sudo nginx -s reload
Reload config — zero downtime ✅
sudo nginx -t
Test config syntax trước khi reload
sudo nginx -T
Dump toàn bộ config đã parsed
sudo systemctl enable nginx
Auto-start khi boot
sudo nginx -s quit
Graceful shutdown (chờ connections kết thúc)

Log Commands

tail -f /var/log/nginx/access.log
Real-time access log
tail -f /var/log/nginx/error.log
Real-time error log
grep " 5[0-9][0-9] " /var/log/nginx/access.log | tail -50
Lọc 5xx errors
awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
Top 20 URLs nhiều request nhất
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10
Top 10 client IPs
sudo nginx -s reopen
Reopen log files (sau logrotate)

Useful One-liners

BASH
# Kiểm tra process đang chạy
ps aux | grep nginx

# Đếm số active connections
ss -tnp | grep nginx | wc -l

# Xem Nginx status (nếu có stub_status module)
curl http://localhost/nginx_status

# Test SSL certificate
openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates

# Check certificate expiry
echo | openssl s_client -connect example.com:443 2>/dev/null   | openssl x509 -noout -enddate

# Benchmark với ab (Apache Benchmark)
ab -n 10000 -c 100 https://example.com/

# Benchmark với wrk
wrk -t4 -c100 -d30s https://example.com/

# Xem open files của Nginx
sudo lsof -p $(cat /var/run/nginx.pid) | head -20

# Kiểm tra file descriptor limit
cat /proc/$(cat /var/run/nginx.pid)/limits | grep "open files"
9
Debug & Troubleshooting
Xử lý các vấn đề thường gặp trong thực tế
🔴 502 Bad Gateway
Nguyên nhân: Backend service không chạy hoặc không reachable.

Debug:
• Check backend process: systemctl status app
• Check port: ss -tlnp | grep :3000
• Check Nginx error log: grep 502 /var/log/nginx/error.log
• Curl trực tiếp backend: curl -v http://localhost:3000
🟡 504 Gateway Timeout
Nguyên nhân: Backend quá chậm, timeout Nginx.

Debug:
• Tăng timeout: proxy_read_timeout 120s;
• Check backend performance
• Check network latency: ping backend-host
• Monitor với: watch -n1 "ss -tnp | grep :3000 | wc -l"
🟠 413 Request Too Large
Nguyên nhân: Upload file vượt client_max_body_size.

Fix:
client_max_body_size 50m; trong server/location block.

Lưu ý: Set đúng context (location upload > server > http)
🔵 CORS Errors
Nguyên nhân: Missing Access-Control headers.

Fix:
• Add add_header Access-Control-Allow-Origin "*" always;
• Handle OPTIONS preflight với if ($request_method = OPTIONS)
• Chú ý: always để include cả error responses

Debug Config Issues

BASH
# BƯỚC 1: Test syntax config
sudo nginx -t

# BƯỚC 2: Xem full config đã parse (detect include issues)
sudo nginx -T 2>&1 | less

# BƯỚC 3: Enable debug logging cho 1 IP cụ thể
# Thêm vào server block:
# error_log /var/log/nginx/debug.log debug;
# events { debug_connection YOUR_IP; }

# BƯỚC 4: Check nếu location đúng bằng return debug
# Tạm thời thêm vào location cần test:
# return 200 "location matched: $uri
host: $host
";
# add_header Content-Type "text/plain";

# BƯỚC 5: Test request với curl verbose
curl -v -H "Host: example.com" http://localhost/api/test

# BƯỚC 6: Check upstream connectivity từ Nginx server
curl -v http://backend-host:3000/health

# BƯỚC 7: Xem real-time logs với filter
tail -f /var/log/nginx/error.log | grep -v "favicon"

Common Config Gotchas

NGINXGotchas cần tránh
# ❌ WRONG: Trailing slash vấn đề với proxy_pass
location /api {
    proxy_pass http://backend;    # /api/v1 → backend/api/v1
}
# ✅ CORRECT: Với trailing slash sẽ strip prefix
location /api/ {
    proxy_pass http://backend/;   # /api/v1 → backend/v1
}

# ❌ WRONG: if là antipattern trong location
location /download/ {
    if ($request_method = POST) {
        proxy_pass http://upload_server;  # CÓ THỂ gây undefined behavior!
    }
}
# ✅ CORRECT: Dùng separate location hoặc limit_except
location /download/ {
    limit_except GET HEAD {
        deny all;
    }
    root /var/files;
}

# ❌ WRONG: add_header không có 'always' sẽ bỏ qua error responses
add_header X-Frame-Options SAMEORIGIN;

# ✅ CORRECT: Dùng 'always' để include trong mọi response code
add_header X-Frame-Options SAMEORIGIN always;

# ❌ WRONG: Nested location với regex sẽ ignore nhau
location /images/ {
    location ~* .php$ { deny all; }  # ĐÂY KHÔNG WORK NHƯ MONG ĐỢI
}
# ✅ CORRECT: Dùng separate top-level location
location ~* /images/.*.php$ {
    deny all;
    return 403;
}
🎯
Quick Reference: Nginx Workflow cho DevOps
Thay đổi config: Edit → nginx -tnginx -s reload
Debug 502: Check backend process → port → firewall → error.log
Debug slow: Check $upstream_response_time trong access log
Deploy zero-downtime: Nginx reload không drop existing connections
Cert renewal: Certbot auto renews + nginx -s reload
⚠️
Production Checklist
server_tokens off — ẩn Nginx version
ssl_protocols TLSv1.2 TLSv1.3 — tắt TLS cũ
☐ Security headers: HSTS, X-Frame-Options, CSP
client_max_body_size — set phù hợp
☐ Rate limiting — chống DDoS/brute force
☐ Log rotation — tránh disk full
☐ Monitoring: /nginx_status → Prometheus/Grafana
☐ Health check endpoint cho upstream servers
Nginx Comprehensive Guide · React port · DevOps Lab