Rate Limiting & Throttling — Security

Kenapa Rate Limiting Penting? Rate limiting membatasi jumlah request yang bisa dikirim client dalam periode waktu tertentu. Tanpa rate limiting, API rentan…

Rate limiting membatasi berapa banyak request yang boleh dikirim satu pengirim dalam satu rentang waktu. Bayangin mesin kopi di kantor yang cuma mau mengeluarkan 5 gelas per 10 detik untuk satu kartu karyawan. Gelas keenam ditolak, dan layarnya bilang "coba lagi 9 detik lagi". Kartunya adalah kunci (IP atau user), 5 per 10 detik adalah batas, dan pesan di layar adalah header Retry-After.

🧪 Tebakan cepat: batasnya 5 request per 10 detik. Kamu mengirim 6 request dalam 1 detik. Request keenam dijawab status apa, dan berapa detik kamu harus menunggu? Tebak dulu, jawabannya ada di bagian "Yang dikirim server saat batas habis".

Kenapa API butuh batas

Tanpa batas, siapa pun bisa mengirim request sebanyak dan secepat yang dia mau:

Rate limiting adalah satu lapisan pertahanan untuk semua itu. Untuk DDoS besar, dia dibantu lapisan lain seperti CDN dan firewall, karena request yang ditolak pun tetap sampai ke server.

Yang dikirim server saat batas habis

Ini jejak dari Lab: batas 5 per 10 detik, dihitung dengan sliding window (cara menghitungnya ada di bagian berikutnya). Waktu dihitung dari request pertama.

Request Waktu Yang lolos di 10 detik terakhir Jawaban server
1 0,0 s 0 200 OK, RateLimit-Remaining: 4
2 0,2 s 1 200 OK, RateLimit-Remaining: 3
3 0,4 s 2 200 OK, RateLimit-Remaining: 2
4 0,6 s 3 200 OK, RateLimit-Remaining: 1
5 0,8 s 4 200 OK, RateLimit-Remaining: 0
6 1,0 s 5 429 Too Many Requests, Retry-After: 9
7 10,1 s 4 (yang di 0,0 s sudah lewat 10 detik) 200 OK, RateLimit-Remaining: 0

Request keenam dijawab 429 Too Many Requests dengan Retry-After: 9: request pertama (di 0,0 s) baru keluar dari jendela 10 detik pada 10,0 s, dan sekarang 1,0 s, jadi sisa 9 detik. Itu jawaban tebakannya.

Tiga cara menghitung

Contoh: batas 5 per 10 detik, lalu 5 request di detik 9,5 dan 5 request lagi di detik 10,5.

Cara Cara kerjanya Di contoh tadi
Fixed window Penghitung per jendela tetap (0 sampai 10 s, 10 sampai 20 s), kembali ke 0 tiap jendela baru 10 lolos dalam 1 detik
Sliding window Menghitung request yang lolos di 10 detik terakhir, dihitung dari sekarang 5 lolos, 5 kena 429
Token bucket Ember isi 5 token, terisi 1 token tiap 2 detik. Tiap request memakai 1 token 5 lolos, lalu 1 lolos tiap 2 detik

Kode sliding window di JavaScript

const LIMIT = 5
const WINDOW_MS = 10_000 // 10 detik
const hits = new Map() // kunci (IP atau user id) -> waktu request yang lolos

function rateLimit(key, now = Date.now()) {
  const recent = (hits.get(key) ?? []).filter((t) => t > now - WINDOW_MS)
  if (recent.length >= LIMIT) {
    const retryAfter = Math.ceil((recent[0] + WINDOW_MS - now) / 1000)
    return { status: 429, headers: { 'Retry-After': String(retryAfter) } }
  }
  recent.push(now)
  hits.set(key, recent)
  return { status: 200, headers: { 'RateLimit-Limit': String(LIMIT), 'RateLimit-Remaining': String(LIMIT - recent.length) } }
}

Di Laravel

// routes/api.php: 60 request per 1 menit
Route::middleware('throttle:60,1')->group(function () {
    Route::get('/data', [DataController::class, 'index']);
});

// AppServiceProvider::boot()
RateLimiter::for('api', function (Request $request) {
    return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});

RateLimiter::for('login', function (Request $request) {
    return Limit::perMinute(5)->by($request->ip());
});

Route::post('/login', [AuthController::class, 'login'])->middleware('throttle:login');

⚠️ Jebakan yang sering ketemu

TL;DR: Rate limiting membatasi request per kunci (IP atau user id) per rentang waktu, untuk menahan brute force, credential stuffing, scraping, dan DoS atau DDoS (banjir request yang menghabiskan resource server). Lewat batas, server menjawab 429 Too Many Requests dengan Retry-After (berapa detik menunggu), dan memberi RateLimit-Limit serta RateLimit-Remaining saat masih boleh. Fixed window punya celah di batas jendela, sliding window menghitung 10 detik terakhir dari sekarang, token bucket mengizinkan burst lalu laju rata. Di Laravel: throttle:60,1, dan Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()). Endpoint login dibuat lebih ketat, dan hitungan disimpan di Redis kalau server lebih dari satu.

Yang akan kamu pelajari