Website và API trên VPS có thể bị tăng tải mạnh chỉ vì bot crawl, brute-force login, retry loop hoặc một client gửi quá nhiều request trong thời gian ngắn. Nginx có module `limit_req` để giới hạn tốc độ xử lý request theo một key như địa chỉ IP. Bài viết này hướng dẫn cách dùng `limit_req_zone`, `limit_req`, `burst`, `nodelay`, `dry_run`, log và status code đúng cách, đồng thời chỉ ra các trường hợp rate limiting dễ chặn nhầm traffic hợp lệ.
Khi website hoặc API trên VPS bị tăng tải, nguyên nhân không phải lúc nào cũng là traffic tốt. Một bot crawl quá nhanh, brute-force login, client lỗi retry liên tục hoặc scraper có thể tạo hàng trăm request mỗi giây vào một endpoint nhỏ.
Nginx có sẵn module ngx_http_limit_req_module để giới hạn tốc độ xử lý request theo một key, phổ biến nhất là IP client. Nginx dùng cơ chế kiểu leaky bucket để kiểm soát request rate, cho phép bạn quy định tốc độ trung bình và mức burst ngắn hạn.
Mục tiêu không phải chặn càng nhiều càng tốt. Mục tiêu là bảo vệ backend khỏi request bất thường nhưng vẫn giữ trải nghiệm bình thường cho người dùng thật.
Nếu bạn đang dùng Nginx làm reverse proxy, xem thêm bài Cài Nginx reverse proxy trên VPS Ubuntu.
Rate limiting phù hợp với trường hợp nào?
Các use case điển hình:
- endpoint login;
- password reset;
- API public;
- search endpoint tốn CPU/database;
- scraper/bot gửi request quá nhanh;
- form submit;
- webhook endpoint bị retry bất thường;
- download endpoint cần hạn chế request rate.
Rate limiting không thay WAF, CAPTCHA hoặc authentication. Nó là một lớp kiểm soát tốc độ request ở Nginx.
Cú pháp cơ bản
Trong block http:
limit_req_zone $binary_remote_addr zone=perip:10m rate=5r/s;
Trong server hoặc location:
location /api/ {
limit_req zone=perip burst=10;
proxy_pass http://backend;
}
Theo tài liệu Nginx, limit_req_zone tạo shared memory zone để lưu trạng thái theo key. Ở ví dụ trên:
- key: IP client;
- zone:
perip; - kích thước: 10 MB;
- tốc độ trung bình: 5 request/giây.
Vì sao dùng $binary_remote_addr?
Nginx khuyến nghị dùng $binary_remote_addr thay vì $remote_addr cho key IP vì biểu diễn nhị phân có kích thước cố định và tiết kiệm memory hơn.
Nó hỗ trợ cả IPv4 và IPv6.
burst có tác dụng gì?
Ví dụ:
limit_req zone=perip burst=10;
Nếu user gửi request nhanh hơn rate, Nginx có thể xếp các request vượt tốc độ vào vùng burst và xử lý dần.
Không có burst, một spike ngắn rất dễ bị reject ngay cả khi người dùng thật chỉ tải trang có nhiều request cùng lúc.
Ví dụ:
rate=5r/s
burst=10
nghĩa là tốc độ trung bình vẫn được kiểm soát ở 5 r/s nhưng có khả năng hấp thụ một đợt request ngắn trong giới hạn burst.
nodelay khác gì?
Mặc định, request dư trong burst có thể bị delay để giữ nhịp xử lý.
Nếu thêm:
limit_req zone=perip burst=10 nodelay;
các request nằm trong burst được xử lý ngay, nhưng vẫn tiêu thụ slot burst. Khi burst đầy, request tiếp theo bị reject.
nodelay thường phù hợp khi bạn muốn user hợp lệ được burst nhanh nhưng vẫn giới hạn client gửi quá nhiều request kéo dài.
Đừng bắt đầu bằng limit quá chặt
Một cấu hình như:
rate=1r/s
burst=1
có thể chặn nhầm:
- SPA gọi nhiều API song song;
- trang web tải nhiều resource động;
- mobile app retry hợp lệ;
- user ở NAT dùng chung một public IP;
- văn phòng có hàng trăm người đi qua cùng gateway.
Hãy đo traffic thực trước khi đặt ngưỡng.
Bật dry-run trước khi chặn thật
Nginx hỗ trợ:
limit_req_dry_run on;
Ở chế độ này, Nginx không thực sự chặn request nhưng vẫn ghi nhận trạng thái request nào lẽ ra bị giới hạn.
Đây là cách rất hữu ích để rollout an toàn:
- cấu hình zone và limit;
- bật
limit_req_dry_run on; - theo dõi log vài ngày;
- điều chỉnh rate/burst;
- mới chuyển dry-run về off.
Dùng status code 429 thay vì 503
Mặc định, limit_req có thể trả status 503 khi reject request.
Với rate limiting API, status 429 thường rõ nghĩa hơn:
limit_req_status 429;
Ví dụ:
location /api/login {
limit_req zone=login burst=5 nodelay;
limit_req_status 429;
proxy_pass http://backend;
}
Client API có thể hiểu 429 là “Too Many Requests” và backoff hợp lý hơn.
Tách zone theo endpoint
Không nên dùng một rate cho toàn bộ website nếu workload khác nhau.
Ví dụ:
limit_req_zone $binary_remote_addr zone=general:10m rate=20r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=2r/s;
Sau đó:
location / {
limit_req zone=general burst=40 nodelay;
proxy_pass http://web;
}
location = /login {
limit_req zone=login burst=5 nodelay;
limit_req_status 429;
proxy_pass http://web;
}
Login cần chặt hơn trang đọc nội dung thông thường.
Giới hạn tổng request cho virtual host
Bạn có thể tạo zone theo server name:
limit_req_zone $server_name zone=perserver:10m rate=200r/s;
Sau đó áp đồng thời per-IP và per-server:
limit_req zone=perip burst=20 nodelay;
limit_req zone=perserver burst=100;
Nginx cho phép nhiều limit_req cùng hoạt động để giới hạn theo nhiều chiều.
Rate limiting phía sau Cloudflare hoặc reverse proxy
Đây là điểm rất dễ cấu hình sai.
Nếu Nginx nhận request từ Cloudflare/load balancer nhưng $remote_addr chỉ thấy IP proxy, mọi user có thể bị gom chung vào một key.
Bạn cần cấu hình real client IP đúng và chỉ trust proxy hợp lệ.
Không được tin bừa header như X-Forwarded-For từ toàn Internet, vì client có thể tự giả mạo nếu Nginx không giới hạn nguồn proxy tin cậy.
Nếu đang dùng Cloudflare Tunnel hoặc proxy phía trước, hãy kiểm tra lại luồng client IP trước khi bật rate limiting theo IP.
Rate limiting không phải DDoS protection toàn diện
Nginx chỉ xử lý request sau khi traffic đã tới VPS.
Nếu đường truyền hoặc network interface bị saturate bởi volumetric attack, Nginx rate limiting không cứu được uplink.
Đối với DDoS lớn cần lớp upstream như CDN/WAF/anti-DDoS network.
Rate limiting tại Nginx phù hợp hơn với:
- application abuse;
- scraper;
- brute force;
- API overuse;
- request flood ở mức VPS còn nhận được traffic.
Kết hợp limit_conn
Ngoài request rate, Nginx có module limit_conn để giới hạn số connection đồng thời theo key.
Ví dụ:
limit_conn_zone $binary_remote_addr zone=conn_per_ip:10m;
Trong location:
limit_conn conn_per_ip 20;
limit_req và limit_conn giải quyết hai bài toán khác nhau:
limit_req: tốc độ request;limit_conn: số connection/request concurrent theo semantics của module.
Không cần bật cả hai nếu workload không yêu cầu.
Log request bị giới hạn
Bạn có thể thay log level:
limit_req_log_level notice;
Nginx cũng có biến:
$limit_req_status
với các giá trị như:
PASSED;DELAYED;REJECTED;- trạng thái dry-run tương ứng.
Bạn có thể thêm vào access log để phân tích trước/sau rollout.
Ví dụ custom log format:
log_format main '$remote_addr $request $status limit=$limit_req_status';
Ví dụ bảo vệ endpoint login
Trong http:
limit_req_zone $binary_remote_addr zone=login_zone:10m rate=5r/m;
Trong server:
location = /login {
limit_req zone=login_zone burst=5 nodelay;
limit_req_status 429;
proxy_pass http://app_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
Ở đây dùng request/minute thay vì request/second vì endpoint login thường không cần tần suất cao.
Đây chỉ là ví dụ. Nếu app có mobile client hoặc SSO, ngưỡng phù hợp có thể khác đáng kể.
Ví dụ bảo vệ search endpoint
Search thường tốn database/CPU hơn trang tĩnh.
limit_req_zone $binary_remote_addr zone=search_zone:10m rate=3r/s;
server {
location /search {
limit_req zone=search_zone burst=10;
limit_req_status 429;
proxy_pass http://backend;
}
}
Không dùng nodelay nếu bạn muốn Nginx làm phẳng burst bằng cách delay request dư thay vì xử lý ngay.
Validate trước khi reload
Sau khi sửa config:
sudo nginx -t
Chỉ khi syntax OK mới reload:
sudo systemctl reload nginx
Reload an toàn hơn restart cho nhiều thay đổi config vì worker cũ có thể phục vụ connection đang tồn tại trong quá trình chuyển đổi.
Test rate limit
Có thể dùng curl loop cho test đơn giản:
for i in $(seq 1 20); do
curl -s -o /dev/null -w '%{http_code}\n' https://example.com/api/test &
done
wait
Chỉ test trên hệ thống bạn có quyền và kiểm soát.
Với load test nghiêm túc, dùng môi trường staging và công cụ phù hợp để tránh tự tạo sự cố production.
Kết hợp monitoring
Sau khi bật rate limit, theo dõi:
- 429/503 count;
- request rate;
- backend latency;
- CPU/RAM;
- database load;
- số request
REJECTEDhoặcDELAYED; - tỷ lệ user report lỗi.
Nếu đã dùng Grafana/Prometheus, bài Giám sát VPS bằng Prometheus và Grafana là nền tảng tốt để kiểm tra rate limiting có thực sự giảm tải hay chỉ làm tăng error rate.
Sai lầm thường gặp
Dùng một limit cho toàn bộ website
API login và trang đọc nội dung có pattern traffic khác nhau.
Không tính NAT/shared IP
Nhiều user thật có thể đi qua cùng một IP public.
Tin X-Forwarded-For từ mọi nguồn
Header này có thể bị giả mạo nếu bạn không giới hạn trusted proxy.
Bật chặn ngay mà không dry-run
Dễ gây 429 hàng loạt trong peak traffic.
Nghĩ rate limit thay được WAF/DDoS protection
Nó chỉ là một lớp kiểm soát ở Nginx, không phải giải pháp cho mọi dạng tấn công.
Checklist triển khai an toàn
- Xác định endpoint thật sự cần giới hạn.
- Đo request rate bình thường và peak.
- Tạo zone bằng
$binary_remote_addrnếu giới hạn theo IP. - Chọn
ratevàbursttheo workload. - Bật
limit_req_dry_run ontrước. - Kiểm tra real client IP nếu có proxy/CDN.
- Cân nhắc status 429.
- Validate
nginx -ttrước reload. - Theo dõi log và metric sau rollout.
- Điều chỉnh nếu user thật bị chặn nhầm.
FAQ
burst có làm tăng rate thực tế không?
Nó cho phép một lượng request vượt rate trong thời gian ngắn theo cơ chế burst. Rate trung bình vẫn được kiểm soát theo zone.
nodelay có bỏ rate limit không?
Không. Nó cho phép request trong burst được xử lý ngay thay vì delay; khi burst hết, request vẫn bị reject.
Nên trả 429 hay 503?
Với API rate limiting, 429 thường diễn đạt đúng ý nghĩa hơn. Nginx cho phép cấu hình limit_req_status.
Có thể giới hạn theo API key thay vì IP không?
Có thể dùng key khác nếu giá trị đó đã được xác thực và đáng tin cậy. Với Nginx OSS, hãy thiết kế cẩn thận để tránh attacker tạo vô số key/cardinality.
Rate limiting có giảm CPU VPS không?
Có thể, nếu CPU cao do request dư thừa tới backend. Nếu bottleneck nằm ở job nội bộ hoặc database query khác, rate limiting không giải quyết gốc rễ.
Kết luận
Nginx rate limiting là công cụ hiệu quả để giảm bot, brute force và API abuse trước khi request chạm backend. Cách triển khai an toàn nhất là đo traffic thật, dùng limit_req_zone, cấu hình burst hợp lý, bật dry-run trước và theo dõi log/metric sau rollout.
Nếu website hoặc API của bạn vẫn thường xuyên chạm giới hạn CPU/RAM sau khi đã kiểm soát request bất thường, có thể tham khảo các gói VPS tại HUONG CLOUD và lựa chọn tài nguyên theo workload thực tế.
Nguồn tham khảo
- NGINX Docs — ngx_http_limit_req_module: https://nginx.org/en/docs/http/ngx_http_limit_req_module.html
- NGINX Docs — ngx_http_limit_conn_module: https://nginx.org/en/docs/http/ngx_http_limit_conn_module.html
Kết luận
Nếu chưa chắc cấu hình hoặc loại dịch vụ nào phù hợp, hãy gửi nhu cầu thực tế để đội ngũ hỗ trợ tư vấn trước khi mua.
Chưa biết nên chọn gói nào?
Xem bảng giá VPS hoặc liên hệ đội ngũ hỗ trợ để chọn cấu hình phù hợp.
Xem bảng giá VPS →