Đã bao giờ bạn "hiên ngang" bước vào một quán phở và "hùng dũng" gọi: cho 100 tô phở tái. 100 tô phở tái gân, 100 tô phở tái nạm gàu, 100 tô phở đặc biệt, 100 tô phở tái không hành, 100 tô phở tái nạm không hành.....Phù.....viết không thôi đã đủ mệt rồi!
Ở bài trước.Chúng ta vừa học:
Authorization.
Server không chỉ hỏi:
"Anh là ai?"
mà còn hỏi:
"Anh được phép làm gì?"
😎
Nhưng giả sử bạn đã:
- đăng nhập đúng,
- có Token hợp lệ,
- có quyền truy cập API,
- không làm gì sai...
thì bạn có thể gọi API:
10 lần?
100 lần?
1.000 lần?
10.000 lần?trong một phút không?
🤔
Server:
"Không nhé."
🤣
Và đó chính là lúc:
API Rate Limiting xuất hiện.
API Rate Limiting là gì?
API Rate Limiting là cơ chế giới hạn số lượng request mà một user, IP, API Key hoặc client được phép gửi đến API trong một khoảng thời gian nhất định.
Ví dụ:
100 requests / phútcó nghĩa là client chỉ được phép gửi tối đa 100 request trong một phút theo chính sách của API.
Nếu vượt quá giới hạn:
429 Too Many Requestscó thể được trả về.
Rate Limiting giúp:
- bảo vệ Server,
- giảm tải Database,
- chống lạm dụng API,
- hạn chế request tự động quá mức,
- tăng tính ổn định của hệ thống.
Ví dụ đời thường nhất 🚦
Hãy tưởng tượng một nhà hàng.
🍜
Nhà hàng có:
100 chỗ ngồiMột người bước vào.
Ăn xong.
Đi ra.
OK.
Nhưng nếu một người chạy vào:
1
2
3
4
5
...
1000 lầntrong vài phút.
Nhân viên:
"Anh vào đây ăn hay tập thể dục vậy?" 😭
🤣
Nhà hàng phải đặt quy định:
"Mỗi người chỉ được gọi tối đa 5 món trong một lần."
Đó chính là tư duy của Rate Limiting.
API cũng vậy.
Server nói:
"Anh có quyền gọi API."
Nhưng:
"Không có nghĩa là anh muốn gọi bao nhiêu lần cũng được."
"Chua thêm" 😆
Rate Limiting giống như:
đèn giao thông.
Bạn được phép đi.
🚗
Nhưng không có nghĩa:
"Được phép đi"
=>
"Muốn chạy 300 km/h lúc nào cũng được."
🤣
API cũng vậy:
Authentication
→ Anh là ai?
Authorization
→ Anh được phép làm gì?
Rate Limiting
→ Anh được phép làm việc đó bao nhiêu lần trong khoảng thời gian này?Ba câu hỏi.
Ba vấn đề khác nhau.
Tại sao API cần Rate Limiting?
Có rất nhiều lý do.
1. Bảo vệ Server 🛡️
Giả sử API:
GET /api/productsbình thường mỗi user gọi:
10 requests/phútNhưng một chương trình nào đó gọi:
100.000 requests/phútServer sẽ phải xử lý một lượng request khổng lồ.
Nếu có nhiều client cùng làm vậy:
😵
Server có thể quá tải.
2. Bảo vệ Database 🗄️
Đây mới là phần rất đáng quan tâm với PHP/MySQL newbie.
API:
GET /api/productscó thể thực hiện:
SELECT ...
FROM products
...Mỗi request:
API
↓
PHP
↓
MySQLMột request không đáng sợ.
Nhưng:
10.000 requeststhì Database có thể phải xử lý:
10.000 querieshoặc nhiều hơn nếu mỗi request thực hiện nhiều query.
Database:
"Tôi chỉ là Database thôi mà..." 😭
🤣
3. Ngăn Abuse
Không phải mọi request đều là request của người dùng bình thường.
Có thể là:
- Bot
- Script tự động
- Crawler
- Tool thử API
- Client bị lỗi
- Một chương trình gọi API quá nhanh
Rate Limiting giúp hạn chế những trường hợp này.
4. Hạn chế Brute Force 🔐
Đây là một ứng dụng rất quan trọng.
Giả sử API login:
POST /api/loginNếu Server cho phép thử:
1.000.000 lần/phút😱
Một kẻ tấn công có thể thử rất nhiều mật khẩu.
Nếu có Rate Limiting:
5 login attempts / phútthì tốc độ thử bị giảm đáng kể.
Rate Limiting không phải toàn bộ giải pháp bảo mật cho login, nhưng là một lớp phòng vệ rất hữu ích.
5. Bảo vệ API khỏi Client bị lỗi 🤦
Không phải lúc nào request quá nhiều cũng do hacker.
Đôi khi chính lập trình viên gây ra.
Ví dụ Frontend có bug:
setInterval(() => {
fetch('/api/products');
}, 10);😱
Cứ mỗi:
10 millisecondslại gọi API.
Server:
GET
GET
GET
GET
GET
GET
...Frontend:
"Em chỉ muốn cập nhật dữ liệu thôi mà."
Server:
"Em đang DDOS anh đấy!" 😭
🤣
Rate Limiting có thể trở thành "phanh khẩn cấp".
Rate Limit hoạt động thế nào?
Ví dụ Server đặt:
100 requests / minuteClient gửi:
Request 1
Request 2
Request 3
...
Request 100→ OK.
Request thứ:
101Server:
429 Too Many Requests🚦
Thông điệp rất rõ:
"Chậm lại anh ơi."
HTTP 429 là gì?
Đây là HTTP Status Code rất đáng nhớ.
429 Too Many RequestsNghĩa đơn giản:
Bạn đã gửi quá nhiều request trong khoảng thời gian được cho phép.
Ví dụ:
GET /api/productsResponse:
HTTP/1.1 429 Too Many RequestsClient nên hiểu:
"Server đang bảo mình giảm tốc độ."
Không phải:
"API chết rồi."
😄
429 khác 401 và 403 thế nào?
Chúng ta đã gặp hai anh:
401
403Bây giờ có thêm:
429| Status | Ý nghĩa đơn giản |
|---|---|
| 401 | Chưa xác thực hợp lệ |
| 403 | Đã xác thực nhưng không có quyền |
| 429 | Gửi quá nhiều request |
Ví dụ:
Không có Token
→ 401Có Token nhưng không có quyền
→ 403Có Token + có quyền nhưng gọi quá nhanh
→ 429😎
Rate Limiting không có nghĩa là "cấm"
Đây là một hiểu lầm khá phổ biến.
Nếu API quy định:
100 requests / phútthì không có nghĩa:
"User chỉ được gọi API 100 lần rồi bị khóa vĩnh viễn."
Không.
Thông thường đó là:
Giới hạn trong một khoảng thời gian.
Ví dụ:
14:00:00 → 100 requests
14:01:00 → quota được tính lạiCách tính chính xác phụ thuộc vào cơ chế Rate Limiting mà hệ thống sử dụng.
Có những kiểu Rate Limiting nào?
Có nhiều thuật toán.
Nghe tên có vẻ hơi "Backend":
- Fixed Window
- Sliding Window
- Token Bucket
- Leaky Bucket
😅
Nhưng đừng hoảng.
Ý tưởng chung vẫn là:
Đếm hoặc kiểm soát tốc độ request trong một khoảng thời gian.
Fixed Window
Ví dụ:
100 requests / phútServer chia thời gian thành từng cửa sổ:
14:00 → 14:01
14:01 → 14:02
14:02 → 14:03Trong mỗi cửa sổ:
Tối đa 100 requestsDễ hiểu.
Dễ triển khai.
Nhưng có một vấn đề ở ranh giới giữa hai cửa sổ.
Ví dụ:
14:00:59
→ 100 requests
14:01:00
→ thêm 100 requests😵
Trong khoảng thời gian rất ngắn có thể xuất hiện lượng request lớn.
Đây là lý do các hệ thống thực tế có thể dùng những cách tinh vi hơn.
Sliding Window
Thay vì nhìn cứng theo:
14:00 → 14:01hệ thống nhìn một khoảng thời gian "trượt" theo thời điểm hiện tại.
Ví dụ:
"Trong 60 giây gần nhất, anh đã gọi bao nhiêu request?"
Cách này giúp kiểm soát mượt hơn trong một số trường hợp.
Token Bucket 🪣
Tên nghe vui đúng không?
Bucket = cái xô.
Hãy tưởng tượng Server có một cái xô chứa:
100 tokenMỗi request lấy:
1 tokenHết token:
"Hết vé."
🤣
Sau đó token được nạp lại theo tốc độ nhất định.
Ví dụ:
Bucket capacity = 100
Refill = 10 token/giâyClient có thể gửi request trong giới hạn số token có sẵn.
Đây là một cách rất phổ biến để mô hình hóa giới hạn tốc độ.
Leaky Bucket 💧
Hãy tưởng tượng một cái xô có lỗ nhỏ.
Request đổ vào.
Nhưng dữ liệu chỉ được xử lý ra với tốc độ nhất định.
Nếu request đến quá nhanh:
Request
Request
Request
Request
Request
...xô có thể đầy.
→ Request mới có thể bị từ chối.
😄
Đây là cách hình dung đơn giản về Leaky Bucket.
Rate Limit dựa trên cái gì?
Đây là câu hỏi rất hay.
Server có thể giới hạn theo:
IP
Ví dụ:
100 requests / minute / IPMột IP gửi quá nhiều request:
→ 429.
User
Ví dụ:
1000 requests / hour / userUser đăng nhập nào có quota riêng.
API Key
Ví dụ:
API Key A → 10.000 requests/day
API Key B → 100.000 requests/dayPhù hợp với các API dành cho developer hoặc ứng dụng bên ngoài.
Endpoint
Có thể mỗi API có giới hạn khác nhau.
Ví dụ:
GET /api/products
→ 1000/minutenhưng:
POST /api/login
→ 5/minuteRất hợp lý.
Vì Login thường cần bảo vệ chặt hơn.
Không phải API nào cũng có cùng Rate Limit
Ví dụ:
GET /api/productsrất nhẹ.
Bạn có thể cho:
1000 requests/minuteNhưng:
POST /api/generate-reportcó thể rất nặng.
Mỗi request mất:
5 giây CPUhoặc truy vấn rất nhiều dữ liệu.
Không thể cho cùng một Rate Limit.
Có thể chỉ cho:
10 requests/minuteĐây là lý do Rate Limiting nên dựa trên đặc điểm của endpoint.
Rate Limit có thể khác nhau theo User
Ví dụ một API SaaS:
Free
→ 100 requests/hour
Pro
→ 10.000 requests/hour
Enterprise
→ 1.000.000 requests/hour😎
Không phải vì Server "thiên vị".
Mà vì:
- Gói dịch vụ khác nhau.
- Tài nguyên sử dụng khác nhau.
- Business model khác nhau.
API nên thông báo Rate Limit cho Client
Một API tốt không nên chỉ nói:
429rồi bỏ mặc Client.
API có thể cung cấp thông tin như:
Limit
Remaining
ResetVí dụ:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 20Ý nghĩa:
Limit = 100
Remaining = 20Client biết:
"Tôi còn 20 request."
Một số API còn cung cấp thời điểm hoặc số giây cần chờ trước khi thử lại, chẳng hạn qua:
Retry-AfterRetry-After là gì?
Giả sử API trả:
429 Too Many Requests
Retry-After: 30Có thể hiểu:
"Đợi khoảng 30 giây rồi thử lại."
😄
Client nên tôn trọng thông tin này thay vì:
429
↓
thử lại ngay
↓
429
↓
thử lại ngay
↓
429
↓
...🤣
Làm vậy chỉ khiến tình hình tệ hơn.
Đây là lúc Exponential Backoff xuất hiện 📈
Một cách phổ biến để Client retry là:
1 giây
2 giây
4 giây
8 giây
16 giây
...Thay vì:
retry
retry
retry
retry
retryliên tục.
Đây gọi là:
Exponential Backoff.
Ý tưởng:
Càng bị từ chối nhiều, càng chờ lâu trước khi thử lại.
Rất hữu ích khi hệ thống đang quá tải hoặc có giới hạn request.
"Chua thêm" lần 3 🤣
Hãy tưởng tượng bạn gọi điện cho một nhà hàng:
"Alo?"
Nhà hàng:
"Đang bận."
Bạn:
"Alo?"
"Đang bận."
"Alo?"
"Đang bận."
Bạn:
"Alo?"
"ĐANG BẬN!!!" 😭
🤣
Cách thông minh hơn:
"OK. Tôi đợi 30 giây rồi gọi lại."
Đó chính là tư duy của:
Retry + Backoff.
Rate Limiting và Pagination có liên quan không?
Có.
Hai kỹ thuật này giải quyết hai vấn đề khác nhau.
Pagination
Giới hạn:
Bao nhiêu dữ liệu trong một response?
Rate Limiting
Giới hạn:
Bao nhiêu request trong một khoảng thời gian?
Ví dụ:
Pagination:
20 products / requestvà:
Rate Limit:
100 requests / minuteBạn có thể có cả hai.
Ví dụ API hoàn chỉnh
Frontend muốn lấy danh sách sản phẩm.
Request:
GET /api/products?page=1&limit=20
Authorization: Bearer TOKENServer kiểm tra:
Bước 1
Token hợp lệ?
→ Authentication.
Bước 2
User có quyền xem products?
→ Authorization.
Bước 3
User có vượt Rate Limit?
→ Rate Limiting.
Bước 4
Pagination hợp lệ?
→ page=1, limit=20.
Bước 5
Query Database.
Bước 6
Trả JSON.
🎉
Ta có:
Request
↓
Authentication
↓
Authorization
↓
Rate Limiting
↓
Pagination
↓
Database
↓
JSON
↓
FrontendĐây chính là một Backend flow rất thực tế.
Rate Limiting đặt ở đâu?
Không nhất thiết chỉ ở PHP.
Có thể triển khai ở nhiều tầng:
Client
↓
CDN / Reverse Proxy
↓
Web Server
↓
Application
↓
DatabaseVí dụ:
- Cloudflare
- Nginx
- API Gateway
- Application Framework
- Redis
- Database-backed counter
Mỗi tầng có thể đóng một vai trò khác nhau.
Với người mới làm PHP:
Đừng cố xây hệ thống Rate Limiting khổng lồ ngay từ đầu.
Hiểu nguyên lý trước.
Sau đó mới học cách triển khai.
Rate Limiting trong PHP
Một cách đơn giản về mặt tư duy:
if ($request_count > $limit) {
http_response_code(429);
exit;
}Nhưng...
😅
Đây chỉ là minh họa ý tưởng.
Trong hệ thống thật, bạn cần:
- xác định client,
- lưu số request,
- xác định time window,
- xử lý đồng thời,
- reset counter,
- tránh race condition,
- chọn storage phù hợp.
Nếu traffic lớn.
Không nên chỉ dựa vào một biến PHP:
$count++;rồi hy vọng mọi thứ ổn.
🤣
PHP request kết thúc thì biến đó cũng biến mất.
Bạn cần một nơi lưu trạng thái phù hợp.
Redis thường xuất hiện ở đây
Trong các hệ thống lớn, bạn có thể gặp:
Redis
để lưu counter/rate-limit state.
Ví dụ tư duy:
User 25
↓
Redis
↓
requests = 87
↓
limit = 100Request tiếp theo:
88→ OK.
...
Request:
101→ 429.
Redis phù hợp với các bài toán cần thao tác counter nhanh, nhưng cách triển khai cụ thể phụ thuộc kiến trúc hệ thống.
Laravel có Rate Limiting không?
Có.
Laravel cung cấp cơ chế hỗ trợ Rate Limiting và Throttling để giới hạn request.
Ví dụ bạn sẽ gặp những khái niệm như:
RateLimiter
throttle middlewareTư duy vẫn giống nhau:
Request
↓
Throttle
↓
Còn quota?
↓
YES → tiếp tục
NO → 429Đây là một trong những lý do Framework giúp developer không phải tự xây mọi thứ từ số 0.
Một lỗi newbie rất dễ mắc 🤦
Bạn test API bằng Postman.
Bấm:
Send
Send
Send
Send
Send
...
Rồi đột nhiên:
429 Too Many RequestsBạn:
"API hỏng rồi!" 😱
Không.
API đang làm đúng nhiệm vụ.
🤣
Hãy kiểm tra:
- Rate Limit bao nhiêu?
- Window bao lâu?
- Có
Retry-Afterkhông? - User/IP nào đang bị giới hạn?
- Có request tự động nào đang chạy không?
Một lỗi còn buồn cười hơn 🤣
Frontend có bug.
Code:
fetch('/api/products');nằm trong một đoạn code được chạy lại mỗi lần component render.
Component render:
1
2
3
4
5
...API:
GET
GET
GET
GET
GET
...Sau một lúc:
429Developer:
"Sao API hôm nay khó ở vậy?"
🤣
Thực ra API đang nói:
"Anh tự sửa Frontend đi."
Đây là lý do khi gặp 429:
đừng vội tăng Rate Limit.
Hãy tìm nguyên nhân request quá nhiều trước.
Rate Limiting có thay thế Security không?
Không.
Rate Limiting chỉ là một lớp bảo vệ.
Nó không thay thế:
- Authentication
- Authorization
- Input Validation
- Prepared Statements
- HTTPS
- Access Control
- Logging
- Monitoring
Một hệ thống an toàn thường có nhiều lớp.
Có thể hình dung:
Authentication
↓
Authorization
↓
Validation
↓
Rate Limiting
↓
Database Security
↓
Logging / MonitoringKhông có "một chiếc khóa thần kỳ" bảo vệ toàn bộ API.
Rate Limiting có thể gây khó chịu không? 😅
Có.
Nếu đặt quá thấp:
10 requests / minutetrong khi Frontend thực sự cần:
30 requests / minutethì user bình thường cũng bị:
429😵
Ngược lại.
Nếu đặt quá cao:
1.000.000 requests / minutethì Rate Limiting gần như vô nghĩa.
Vì vậy phải cân bằng:
Đủ chặt để bảo vệ hệ thống, đủ rộng để user bình thường không bị vướng.
Rate Limit nên thiết kế thế nào?
Không có một con số thần kỳ.
Hãy xem:
1. Endpoint làm gì?
Đọc dữ liệu đơn giản?
Hay xử lý nặng?
2. User bình thường gọi bao nhiêu?
Đừng đoán.
Hãy quan sát.
3. Database chịu được bao nhiêu?
4. Có Cache không?
5. Có nhiều loại User không?
6. Có API public không?
Public API thường cần kiểm soát rất kỹ.
Debug 429 kiểu dev thật 🔍
Khi gặp:
429 Too Many Requestsđừng ngay lập tức:
"Tăng limit lên!" 😭
Hãy làm từng bước.
1. Kiểm tra Network
Mở:
DevTools → Network
Xem API bị gọi bao nhiêu lần.
2. Kiểm tra Request
Ví dụ:
GET /api/productsCó bị gọi lặp lại không?
3. Kiểm tra Response Headers
Tìm:
RateLimit
X-RateLimit
Retry-Afternếu API cung cấp.
4. Kiểm tra khoảng thời gian
Ví dụ:
100 requests / minuteBạn có thực sự gửi hơn 100 request không?
5. Kiểm tra Frontend
Có:
loop
timer
retry
polling
component re-rendergây request lặp không?
6. Kiểm tra Backend
Có endpoint nào vô tình gọi chính nó hoặc gọi API khác liên tục không?
Một bài học rất quan trọng
Khi API trả:
429đừng chỉ nghĩ:
"Server giới hạn tôi."
Hãy nghĩ:
"Tại sao tôi đang gửi nhiều request đến mức Server phải giới hạn tôi?"
Đây là tư duy Debug.
Không chỉ chữa triệu chứng.
Mà tìm nguyên nhân.
😎
Checklist chuẩn không cần chỉnh 🚦
Rate Limiting = giới hạn số request trong một khoảng thời gian.
429 Too Many Requests= gửi quá nhiều request.Authentication không thay thế Rate Limiting.
Authorization không thay thế Rate Limiting.
Pagination giới hạn dữ liệu mỗi response.
Rate Limiting giới hạn tốc độ request.
Có thể giới hạn theo IP, User, API Key hoặc Endpoint.
Login thường cần Rate Limiting chặt hơn các API đọc dữ liệu.
Có thể dùng
Retry-Afterđể báo Client thời gian chờ.Client nên sử dụng Backoff khi retry.
Không nên retry 429 liên tục.
Frontend bug cũng có thể tạo ra hàng nghìn request.
Đừng vội tăng Rate Limit khi gặp 429.
Kiểm tra DevTools → Network khi debug.
Với hệ thống lớn, Rate Limiting có thể được triển khai ở nhiều tầng.
Redis thường được sử dụng cho các bài toán counter/rate-limit state.
Rate Limiting chỉ là một lớp trong API Security.
FAQ nhanh
API Rate Limiting là gì?
→ Là cơ chế giới hạn số request mà một client được phép gửi trong một khoảng thời gian.
429 là lỗi gì?
→ 429 Too Many Requests, nghĩa là client đã gửi quá nhiều request trong khoảng thời gian được cho phép.
Rate Limiting có phải là Authorization không?
→ Không. Authorization quyết định bạn có quyền làm gì; Rate Limiting quyết định bạn được gọi API với tần suất như thế nào.
Có Token hợp lệ thì có bị Rate Limit không?
→ Có. Authentication thành công không đồng nghĩa với việc bạn được gọi API không giới hạn.
Rate Limiting có chống được DDoS không?
→ Nó có thể giúp giảm hoặc kiểm soát một số dạng lưu lượng quá mức, nhưng không phải giải pháp đầy đủ để chống DDoS. Hệ thống lớn thường cần nhiều lớp bảo vệ khác.
Có nên đặt Rate Limit càng cao càng tốt không?
→ Không. Quá cao thì mất tác dụng bảo vệ; quá thấp thì user bình thường cũng bị 429.
Frontend có thể gây 429 không?
→ Có. Một bug khiến Frontend gọi API lặp liên tục là nguyên nhân khá thực tế.
Khi gặp 429 có nên gọi lại ngay không?
→ Không nên. Hãy kiểm tra Retry-After nếu có và sử dụng chiến lược retry/backoff phù hợp.
Bạn có thể cũng đang gặp 😭
👉 API là gì? (Giải thích như gọi đồ ăn.)
👉 Session vs Cookie - Hai anh em dễ nhầm nhất của PHP
Tổng kết
Đến đây chúng ta đã có một chuỗi rất thú vị:
Authentication
↓
"Anh là ai?"
↓
Authorization
↓
"Anh được phép làm gì?"
↓
Pagination
↓
"Anh cần bao nhiêu dữ liệu?"
↓
Rate Limiting
↓
"Anh gọi bao nhiêu lần?"😎
Đây là tư duy Backend rất quan trọng.
Một API tốt không chỉ:
"Có dữ liệu là trả dữ liệu."
Mà phải suy nghĩ:
Ai đang gọi?
Người đó có quyền không?
Cần trả bao nhiêu dữ liệu?
Được gọi bao nhiêu lần?
Nếu gọi quá nhanh thì sao?
Nếu Client bị lỗi thì sao?
Nếu có 100 người cùng gọi thì sao?
Nếu có 100.000 người thì sao?
Đó là lúc chúng ta bắt đầu chuyển từ:
"Code chạy được."
sang:
"Code chạy được trong thế giới thật." 😎
Và đó mới là điều DatVietLapTrinh đang muốn hướng tới.