Proxy, TLS, WebSocket và rate limit xem Nginx reverse proxy với TLS; app SPA dưới prefix xem Vite app dưới URI prefix.
CLI
| Mục đích | Lệnh |
|---|---|
| Kiểm tra cú pháp | sudo nginx -t |
In toàn bộ cấu hình đã gộp mọi include |
sudo nginx -T |
| Tìm server block đang xử lý một domain | sudo nginx -T 2>/dev/null | grep -n 'server_name' |
| Reload không ngắt kết nối | sudo nginx -t && sudo systemctl reload nginx (hoặc nginx -s reload) |
| Phiên bản, module và tham số build | nginx -V |
| Dùng file cấu hình khác để thử | nginx -t -c /tmp/test.conf |
| Mở lại file log (sau khi logrotate) | nginx -s reopen |
| Log lỗi | tail -f /var/log/nginx/error.log |
Thứ tự match location
| Bước | Cú pháp | Quy tắc |
|---|---|---|
| 1 | location = /path |
Khớp chính xác: dừng ngay |
| 2 | location ^~ /static/ |
Nếu prefix dài nhất khớp có ^~: dừng, không xét regex |
| 3 | location ~ \.php$ / ~* (không phân biệt hoa thường) |
Regex xét theo thứ tự xuất hiện, khớp cái đầu tiên là dùng |
| 4 | location /path |
Không regex nào khớp thì dùng prefix dài nhất đã nhớ ở bước 2 |
location / |
Prefix ngắn nhất, khớp mọi thứ còn lại |
location = /healthz { return 200 "ok\n"; } # 1: chính xác
location ^~ /static/ { root /srv/www; } # 2: prefix chặn regex
location ~* \.(png|jpg|css|js)$ { expires 7d; } # 3: regex
location /api/ { proxy_pass http://127.0.0.1:8000; } # 4: prefix
location / { try_files $uri $uri/ /index.html; }/static/app.js vào khối ^~ /static/, không vào regex ảnh/css dù regex cũng khớp. Bỏ ^~ thì regex thắng.
try_files, root, alias
| Mục đích | Cấu hình |
|---|---|
SPA: file có thật thì trả, không thì về index.html |
try_files $uri $uri/ /index.html; |
| File tĩnh hoặc chuyển cho app | try_files $uri @app; + location @app { proxy_pass http://127.0.0.1:8000; } |
| Không có file thì 404 (không fallback) | try_files $uri =404; |
root: nối nguyên URI vào sau |
location /img/ { root /srv; } → /img/a.png đọc /srv/img/a.png |
alias: thay phần khớp location |
location /img/ { alias /srv/images/; } → /img/a.png đọc /srv/images/a.png |
| Liệt kê thư mục | autoindex on; |
Với alias, dấu / cuối của location và của alias phải khớp nhau. location /img { alias /srv/images/; } (thiếu / ở location) cho phép /img../ đọc ra ngoài thư mục (path traversal).
return và rewrite
| Mục đích | Cấu hình |
|---|---|
| Redirect cả site sang HTTPS | return 301 https://$host$request_uri; |
| Đổi domain giữ path | return 301 https://example.com$request_uri; |
| Trả nội dung trực tiếp | return 200 "ok\n"; (kèm default_type text/plain;) |
| Chặn | return 403; / return 444; (đóng kết nối không trả gì) |
| Redirect một path, giữ query string | location = /old { return 301 /new$is_args$args; } |
| Rewrite có capture | rewrite ^/blog/(\d+)$ /posts/$1 permanent; |
| Rewrite nội bộ rồi tìm location mới | rewrite ^/v1/(.*)$ /api/$1 last; |
| Rewrite trong location hiện tại, không tìm lại | rewrite ^/api/(.*)$ /$1 break; |
Cờ rewrite: last (dừng, tìm location lại), break (dừng, ở lại location), redirect (302), permanent (301). Redirect đơn giản thì ưu tiên return, nhanh và dễ đọc hơn.
Log format
log_format timing '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" "$http_user_agent" '
'rt=$request_time uct=$upstream_connect_time urt=$upstream_response_time ua=$upstream_addr';
log_format json escape=json '{"time":"$time_iso8601","ip":"$remote_addr","host":"$host",'
'"req":"$request","status":$status,"bytes":$body_bytes_sent,'
'"rt":$request_time,"urt":"$upstream_response_time","ua":"$upstream_addr"}';
access_log /var/log/nginx/access.log timing;| Mục đích | Cấu hình / lệnh |
|---|---|
| Log riêng cho một server | access_log /var/log/nginx/app.access.log json; trong server |
| Tắt log cho healthcheck | location = /healthz { access_log off; return 200; } |
| Log có điều kiện (chỉ lỗi) | map $status $loggable { ~^[23] 0; default 1; } + access_log ... if=$loggable; |
| Debug chi tiết một lúc | error_log /var/log/nginx/error.log debug; (cần build --with-debug, xem nginx -V) |
| Request chậm nhất | awk '{for(i=1;i<=NF;i++) if($i ~ /^rt=/) print substr($i,4), $7}' access.log | sort -rn | head |
| Đếm status code | awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn (format combined) |
$request_time: tổng thời gian từ byte đầu tiên của client tới khi gửi xong. $upstream_response_time: chỉ phần chờ upstream. rt lớn mà urt nhỏ thường là client/mạng chậm.
Biến và directive hay dùng
| Mục đích | Cấu hình |
|---|---|
| Host client gửi / tên server khớp | $host / $server_name |
| URI gốc kèm query / URI đã chuẩn hoá | $request_uri / $uri |
| Server mặc định cho Host lạ | listen 80 default_server; server_name _; return 444; |
| Giới hạn upload | client_max_body_size 50m; |
| Ẩn phiên bản nginx | server_tokens off; |
| Chỉ cho phép IP nhất định | allow 203.0.113.0/24; deny all; |
| Basic auth | auth_basic "Restricted"; auth_basic_user_file /etc/nginx/.htpasswd; |
| Gộp đoạn cấu hình dùng chung | include snippets/security-headers.conf; |
Tránh if trong location cho logic phức tạp ("if is evil"): chỉ return và rewrite ... last trong if là an toàn. Dùng map ở khối http để chọn giá trị theo biến.