Rate Limiting membatasi jumlah request yang bisa dilakukan dalam periode waktu tertentu. Ini melindungi sistem dari abuse, DDoS, dan overload.
Mengapa Rate Limiting penting:
Tanpa rate limiting:
Satu user jahat → 100.000 request/detik → Server crash → Semua user terkena dampak
Dengan rate limiting:
User A → 100 req/menit ✓
User B (jahat) → 101 req → Blocked (429 Too Many Requests)
Server tetap sehat untuk semua user
Algoritma Rate Limiting:
1. Fixed Window Counter
Window: setiap menit
Limit: 100 request/menit
00:00 - 00:59 → Counter: 100 → Full, block!
01:00 reset → Counter: 0 → Boleh lagi
Masalah: Burst di batas window!
00:59 → 100 request (window 1 habis)
01:00 → 100 request (window 2 mulai)
= 200 request dalam 2 detik!
2. Sliding Window Log
class SlidingWindowLog {
constructor(limit, windowMs) {
this.limit = limit;
this.windowMs = windowMs;
this.requests = {}; // userId → [timestamps]
}
isAllowed(userId) {
const now = Date.now();
const windowStart = now - this.windowMs;
if (!this.requests[userId]) this.requests[userId] = [];
// Hapus request lama di luar window
this.requests[userId] = this.requests[userId]
.filter(ts => ts > windowStart);
if (this.requests[userId].length >= this.limit) return false;
this.requests[userId].push(now);
return true;
}
}
3. Token Bucket
Bucket berisi token. Setiap request mengambil 1 token. Token diisi kembali dengan rate konstan.
class TokenBucket {
constructor(capacity, refillRate) {
this.capacity = capacity; // max token
this.tokens = capacity; // mulai penuh
this.refillRate = refillRate; // token per detik
this.lastRefill = Date.now();
}
isAllowed() {
this.refill();
if (this.tokens >= 1) {
this.tokens -= 1;
return true;
}
return false;
}
refill() {
const now = Date.now();
const elapsed = (now - this.lastRefill) / 1000; // detik
const newTokens = elapsed * this.refillRate;
this.tokens = Math.min(this.capacity, this.tokens + newTokens);
this.lastRefill = now;
}
}
// Contoh: max 10 burst, 2 token/detik
const limiter = new TokenBucket(10, 2);
Keunggulan Token Bucket: membolehkan burst sesaat (cocok untuk API yang butuh burst tapi rata-rata rendah).
4. Leaky Bucket
Request masuk ke "bucket", keluar dengan rate konstan. Request berlebih ditolak atau diqueue.
Request masuk → [Bucket] → Keluar dengan rate tetap (10 req/detik)
Jika bucket penuh → Tolak request baru (rate limiting)
Perbandingan algoritma:
| Algoritma | Burst | Memory | Akurasi |
|---|---|---|---|
| Fixed Window | Bermasalah di batas | O(1) | Sedang |
| Sliding Window Log | Tidak ada burst anomaly | O(n) | Tinggi |
| Token Bucket | Burst diperbolehkan | O(1) | Tinggi |
| Leaky Bucket | Tidak ada burst | O(n) | Tinggi |
Implementasi di berbagai level:
// Level 1: Per IP
limiter.check(req.ip);
// Level 2: Per User (setelah auth)
limiter.check("user:" + req.user.id);
// Level 3: Per API Key
limiter.check("apikey:" + req.headers["x-api-key"]);
// Level 4: Per Endpoint (endpoint sensitif lebih ketat)
limiter.check("user:" + userId + ":POST:/api/payments");
Response saat di-rate-limit:
// HTTP 429 Too Many Requests
res.status(429).json({
error: "Terlalu banyak request",
message: "Coba lagi dalam 60 detik",
retryAfter: 60, // detik
limit: 100, // limit
remaining: 0, // sisa request
resetAt: "2024-01-15T10:31:00Z"
});
// Header standar
res.set("X-RateLimit-Limit", 100);
res.set("X-RateLimit-Remaining", 0);
res.set("X-RateLimit-Reset", resetTimestamp);
res.set("Retry-After", 60);
Distributed Rate Limiting:
// Dengan Redis — rate limiting yang dibagikan antar semua server
async function isAllowed(userId) {
const key = "ratelimit:" + userId;
const count = await redis.incr(key);
if (count === 1) {
await redis.expire(key, 60); // window 60 detik
}
return count <= 100; // limit 100
}
Rate limiting adalah komponen wajib untuk API publik, mencegah abuse sekaligus menjaga QoS (Quality of Service) untuk semua pengguna.
🎭 Analogi sehari-hari: Bouncer di klub. Total kapasitas 200 orang. Orang masuk-keluar terus. Bouncer cek: "Sekarang ada berapa di dalam? Belum 200? Masuk." Atau: "Tiap menit max 10 orang masuk." Algoritma rate limit = aturan bouncer.
💡 Algoritma rate limit klasik:
- Token Bucket — bucket isi N token, request konsumsi 1 token. Refill berkala. Allow burst sampai bucket size
- Leaky Bucket — request masuk antrian, keluar dengan rate tetap. Smooth output rate
- Fixed Window — count per N detik. Simple tapi ada edge "window boundary burst"
- Sliding Window — count over rolling N detik. Lebih akurat, lebih mahal compute
Most popular: Token Bucket (allow burst) atau Sliding Window Counter (akurat).
⚠️ Jebakan klasik:
- Rate limit per server, bukan per user = scaling rusak (user bisa retry ke server lain)
- In-memory state = tiap server punya counter sendiri. Pakai Redis shared
- Limit terlalu ketat = legitimate user kena. Whitelist + tier-based limit
- Gak return Retry-After header = client gak tau kapan boleh retry, hammer terus
- Lupa rate limit untuk auth endpoint = brute force password attack
🎯 Strategi rate limit by endpoint:
- Auth (login/signup) → strict (5/menit per IP) cegah brute force
- Public API (read) → moderate (100/menit per user)
- Heavy compute → tight (10/menit per user)
- Internal service-to-service → no limit (atau tier khusus)
TL;DR: Rate limit = pelindung dari abuse + DDoS + overload. Token Bucket (burst) atau Sliding Window (akurat) populer. Pakai Redis untuk shared state across server. Strict di auth endpoint. Wajib Retry-After header.