Rate limiting bảo vệ API khỏi spam, brute-force và DDoS.
- Fixed window: đếm requests trong window cố định (0:00-0:15) — dễ bypass bằng cách gửi burst ở cuối window cũ + đầu window mới.
- Sliding window: window trượt theo thời gian thực, chính xác hơn nhưng cần Redis.
express-rate-limitdùng fixed window:rateLimit({ windowMs: 15601000, max: 100, standardHeaders: true, legacyHeaders: false }). - Distributed rate limiting với Redis:
rate-limit-redisstore để share state giữa nhiều server instances — bắt buộc trong cluster/multi-server deployment, nếu dùng in-memory thì mỗi instance có counter riêng, giới hạn thực tế làmax * numInstances. - Rate limit headers trả về client:
RateLimit-Limit,RateLimit-Remaining,RateLimit-Reset(standardHeaders: true). - Limits khác nhau per endpoint: auth endpoints (5 req/15min), API endpoints (100 req/15min), public endpoints (1000 req/15min).
- Per-user limits:
keyGenerator: (req) => req.user?.id || req.ip— authenticated users có limit riêng theo userId thay vì IP. - Lưu ý: behind reverse proxy thì
req.iplà IP của proxy — cầnapp.set('trust proxy', 1)để lấy IP thật từX-Forwarded-For.
Rate limiting protects APIs from spam, brute-force attacks, and DDoS.
- Fixed window: counts requests within a fixed window (0:00-0:15) — easy to bypass by sending a burst at the end of the old window and beginning of the new one.
- Sliding window: window slides in real time, more accurate but requires Redis.
express-rate-limituses a fixed window:rateLimit({ windowMs: 15601000, max: 100, standardHeaders: true, legacyHeaders: false }). - Distributed rate limiting with Redis:
rate-limit-redisstore to share state across multiple server instances — required in cluster/multi-server deployments; with in-memory storage, each instance has its own counter so the real limit ismax * numInstances. - Rate limit headers returned to client:
RateLimit-Limit,RateLimit-Remaining,RateLimit-Reset(with standardHeaders: true). - Different limits per endpoint: auth endpoints (5 req/15min), API endpoints (100 req/15min), public endpoints (1000 req/15min).
- Per-user limits:
keyGenerator: (req) => req.user?.id || req.ip— authenticated users get limits by userId rather than IP.
Pitfall: behind a reverse proxy req.ip is the proxy's IP — set app.set('trust proxy', 1) to get the real IP from X-Forwarded-For.