Nginx from Zero to Production · Part 5 — Production Config & Debugging
Tie it together: a complete real-world config (SPA + API proxy + cache + TLS), reusable includes, Docker & Compose deploy, worker tuning, a debugging cheat-sheet for the errors you will actually hit, and a capstone project.
Bạn đã học từng mảnh: mô hình config, reverse proxy, cân bằng tải, TLS, caching, giới hạn tốc độ. Phần cuối này ghép chúng thành một setup production, ship bằng Docker, và dạy bạn debug Nginx khi (không phải nếu) có gì hỏng.
1. Một config production hoàn chỉnh
Đây là bố cục full-stack kinh điển: SPA tĩnh ở /, API proxy ở /api, có cache, qua HTTPS.
Trước tiên, hai snippet tái dùng để khỏi lặp lại:
# /etc/nginx/snippets/proxy.conf — shared proxy headers
proxy_http_version 1.1;
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_connect_timeout 5s;
proxy_read_timeout 60s;
# /etc/nginx/snippets/security.conf — shared security headers
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
Giờ là site, đọc từ trên xuống:
# http context
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:10m
max_size=1g inactive=60m;
limit_req_zone $binary_remote_addr zone=api:10m rate=20r/s;
upstream app_pool {
least_conn;
server 127.0.0.1:3000 max_fails=3 fail_timeout=10s;
server 127.0.0.1:3001 max_fails=3 fail_timeout=10s;
}
# Redirect all HTTP → HTTPS
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name example.com;
ssl_certificate /etc/nginx/certs/local.crt;
ssl_certificate_key /etc/nginx/certs/local.key;
ssl_protocols TLSv1.2 TLSv1.3;
include snippets/security.conf;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
root /var/www/spa; # built SPA files (index.html + /assets)
# 1) Long-cache fingerprinted assets
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# 2) API → proxy to the backend pool, cached + rate-limited
location /api/ {
limit_req zone=api burst=40 nodelay;
include snippets/proxy.conf;
proxy_pass http://app_pool;
proxy_cache api_cache;
proxy_cache_valid 200 30s;
proxy_cache_use_stale error timeout updating;
proxy_cache_bypass $cookie_session; # never cache logged-in users
add_header X-Cache-Status $upstream_cache_status always;
}
# 3) Everything else → SPA, with client-side routing fallback
location / {
try_files $uri $uri/ /index.html;
}
}
Mọi directive ở đây đến từ Phần 1–4. Đó chính là ý chính — config production chỉ là những thứ cơ bản, ghép lại.
2. Triển khai bằng Docker & Compose
Trên production bạn hiếm khi cài Nginx bằng tay — bạn chạy nó như một container. Image nginx chính thức đọc mọi thứ bạn thả vào /etc/nginx/conf.d/.
# docker-compose.yml — Nginx in front of two app replicas
services:
app:
build: ./app
deploy:
replicas: 2 # two backend instances for the upstream pool
expose:
- "3000" # internal only — NOT published to the host
nginx:
image: nginx:1.27-alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro # your site configs
- ./nginx/certs:/etc/nginx/certs:ro # TLS certs
- ./spa/dist:/var/www/spa:ro # built SPA
depends_on:
- app
Bên trong mạng Docker, các service gọi nhau bằng tên, nên upstream trỏ tới app:3000 thay vì IP:
upstream app_pool {
server app:3000; # Docker DNS resolves "app" to the replicas
}
Khởi động và reload config mà không restart container:
docker compose up -d
docker compose exec nginx nginx -t # validate inside the container
docker compose exec nginx nginx -s reload # graceful reload
Mount config chỉ-đọc (
:ro) và reload thay vì rebuild — đổi config không nên cần image mới.
3. Tinh chỉnh worker & kết nối
Mặc định ổn cho hầu hết site; chỉ tinh chỉnh khi có số liệu trong tay:
worker_processes auto; # = number of CPU cores (don't overthink it)
events {
worker_connections 4096; # max connections PER worker
multi_accept on; # accept many new connections at once
}
http {
keepalive_timeout 65; # reuse client connections (saves handshakes)
sendfile on; # kernel-level file sending (fast static files)
tcp_nopush on; # send headers + file in fewer packets
}
Quy tắc ước lượng dung lượng: worker_processes × worker_connections ≈ số kết nối đồng thời tối đa. Với 4 core × 4096 bạn giữ được ~16k kết nối — quá đủ trước khi cần scale ngang.
Cũng nâng giới hạn file-descriptor của OS: mỗi kết nối là một file descriptor, nên ulimit -n phải lớn hơn mục tiêu kết nối.
4. Cheat-sheet debug
Khi có gì hỏng, làm theo thứ tự này.
Bước 1 — kiểm tra config:
nginx -t # always start here; reports the exact file + line of a syntax error
nginx -T # dump the FULL effective config (all includes merged) — great for "is my change even loaded?"
Bước 2 — đọc error log:
tail -f /var/log/nginx/error.log
# raise verbosity temporarily if needed:
error_log /var/log/nginx/error.log debug; # then reload
Bước 3 — khớp triệu chứng với nguyên nhân:
| Triệu chứng | Nguyên nhân thường gặp |
|---|---|
| 403 Forbidden | Sai root, thiếu index, hoặc quyền file (worker không đọc được) |
| 404 on a SPA route | Thiếu try_files ... /index.html |
| 502 Bad Gateway | Backend chết / sai host:port proxy_pass |
| 504 Gateway Timeout | Backend quá chậm → tăng proxy_read_timeout |
| 413 Request Entity Too Large | client_max_body_size quá nhỏ |
| CSS arrives as text/plain | Thiếu include mime.types; |
| WebSocket connects then drops | Thiếu header Upgrade/Connection |
| Change had no effect | Sửa nhầm file, hoặc quên nginx -s reload |
Lỗi 502 bạn gặp nhiều nhất: nó gần như luôn nghĩa là Nginx ổn, backend mới có vấn đề. Kiểm tra backend đang chạy và proxy_pass trỏ đúng địa chỉ:
curl -v http://127.0.0.1:3000/ # can NGINX'S host reach the backend directly?
# in Docker, exec INTO the nginx container and curl the service name:
docker compose exec nginx wget -qO- http://app:3000/
5. Dự án tổng kết
Tự xây toàn bộ. Cái này chứng minh bạn làm được việc Nginx thật.
Mục tiêu: một domain HTTPS duy nhất phục vụ SPA, proxy API tới hai replica backend có cache + giới hạn tốc độ, và sống sót khi một backend chết.
Yêu cầu:
- Hai instance backend sau một
upstreamvớileast_connvà health check bị động. /phục vụ một SPA đã build với dự phòngtry_files./api/được proxy, cache 30s vớiX-Cache-Status, và giới hạn tốc độ.- HTTPS với redirect HTTP→HTTPS và các snippet header dùng chung.
- Toàn bộ stack chạy bằng
docker compose up.
Bài kiểm tra nghiệm thu:
curl -kI https://localhost/ # HTTP/2 200, security headers present
curl -kI http://localhost/ # 301 → https
curl -k https://localhost/api/ping # rotates between the two replicas
curl -kI https://localhost/api/ping # X-Cache-Status: HIT on the 2nd call
# kill one replica → API still responds (failover)
# blast /api/ in a loop → eventually 503 (rate limit works)
Nếu tất cả pass, bạn có thể cấu hình Nginx cho production.
6. Tóm tắt series
- Phần 1 — Nginx là gì, mô hình worker, cài đặt, site tĩnh đầu tiên.
- Phần 2 — mô hình config: context, khớp
location,try_files, virtual host. - Phần 3 — reverse proxy, header chuyển tiếp, upstream, cân bằng tải, WebSocket.
- Phần 4 — TLS/HTTP2, nén, caching, giới hạn tốc độ, header bảo mật.
- Phần 5 — ghép tất cả, deploy Docker, tinh chỉnh, debug, dự án tổng kết.
Bạn bắt đầu khi còn không biết nginx.conf nằm đâu. Giờ bạn dựng được một reverse proxy bảo mật, có cache, cân bằng tải và debug nó giữa lúc dầu sôi lửa bỏng. Đó là Nginx production.
Bài tập
- Lắp ráp config: xây block
serverđầy đủ ở §1 với hai snippetincludevà xác nhậnnginx -tpass. - Trút sự thật: chạy
nginx -Tvà tìm dòngproxy_cache_pathcủa bạn trong output đã gộp. - Đóng gói Docker: viết
docker-compose.yml, mount config:ro, và reload bằngdocker compose exec nginx nginx -s reload. - Ép một lỗi 502: trỏ
proxy_passtới một cổng chết, tái hiện 502, rồi đọc dòng error log giải thích nó. - Ép một lỗi 403:
chmod 000fileindex.html, tái hiện 403, và xác nhận nguyên nhân trong error log. - Dự án tổng kết: hoàn thành dự án ở §5 và pass mọi bài kiểm tra nghiệm thu.