Rate limiting
Tanpa pembatasan laju, satu skrip bisa menebak password sepanjang malam, mengeruk seluruh isi katalogmu, atau membuat database bekerja keras untuk permintaan yang tidak pernah dilihat manusia.
Intisari
- Definisikan limiter bernama di
AppServiceProvider::boot(), pakai lewat middlewarethrottle:nama. - Batas bisa berbeda per pengguna: pengguna berlangganan mendapat kuota lebih besar dari tamu.
- Melampaui batas menghasilkan 429 beserta header
Retry-After. - Butuh cache bersama. Dengan driver
file, tiap kontainer punya hitungannya sendiri. - Di belakang ALB, pembatasan per-IP hanya benar kalau trusted proxy sudah dikonfigurasi.
Mendefinisikan limiter
// AppServiceProvider::boot()
use Illuminate\Cache\RateLimiting\Limit;
RateLimiter::for('api', fn (Request $r) =>
Limit::perMinute(60)->by($r->user()?->id ?: $r->ip()));
// Batas berbeda per jenis pengguna
RateLimiter::for('unggah', function (Request $r) {
return $r->user()?->berlangganan
? Limit::perMinute(100)->by($r->user()->id)
: Limit::perMinute(10)->by($r->user()?->id ?: $r->ip());
});
// Beberapa batas sekaligus: cepat dalam semenit, dan total dalam sehari
RateLimiter::for('pencarian', fn (Request $r) => [
Limit::perMinute(30)->by($r->ip()),
Limit::perDay(1000)->by($r->ip()),
]);
// Respons kustom
RateLimiter::for('login', fn (Request $r) =>
Limit::perMinute(5)
->by(Str::lower($r->input('email')).'|'.$r->ip())
->response(fn () => response()->json([
'message' => 'Terlalu banyak percobaan. Coba lagi sebentar.',
], 429)));
Route::middleware('throttle:api')->group(...);
Route::post('/login', ...)->middleware('throttle:login');
Route::post('/unggah', ...)->middleware('throttle:unggah');
// Tanpa limiter bernama: 60 permintaan per 1 menit
Route::middleware('throttle:60,1')->group(...);
Kunci pembatas untuk login sebaiknya menggabungkan email dan IP. Kalau kuncinya hanya email, penyerang bisa mengunci akun orang lain hanya dengan mengirim password salah berulang kali โ penolakan layanan terhadap pengguna sah. Kalau hanya IP, satu kantor yang berbagi IP akan saling mengganggu. Menggabungkan keduanya menyeimbangkan kedua risiko itu.
Yang wajib dibatasi
| Endpoint | Saran | Alasan |
|---|---|---|
| Login | 5 / menit per email+IP | Serangan tebak password |
| Lupa password | 3 / menit per IP | Membanjiri kotak masuk orang lain |
| Registrasi | 3 / menit per IP | Pembuatan akun massal |
| Pencarian | 30 / menit | Query mahal; sasaran empuk pengerukan |
| Unggah berkas | 10 / menit | Biaya penyimpanan dan pemrosesan |
| API publik | 60 / menit per token | Keadilan antar konsumen |
| Endpoint yang memanggil layanan berbayar | Seketat mungkin | Setiap permintaan adalah uang |
Memakainya di luar HTTP
use Illuminate\Support\Facades\RateLimiter;
// Batasi pemanggilan API pihak ketiga dari dalam job
$dieksekusi = RateLimiter::attempt(
key: 'sinkron-marketplace',
maxAttempts: 10,
callback: fn () => $klien->kirim($data),
decaySeconds: 60,
);
if (! $dieksekusi) {
$this->release(RateLimiter::availableIn('sinkron-marketplace')); // job dikembalikan ke antrean
}
Pola release() ini penting untuk worker: alih-alih menggagalkan job karena penyedia membatasi laju,
job dikembalikan ke antrean dan dicoba lagi tepat setelah kuotanya pulih.
Dua syarat supaya benar-benar bekerja
| Syarat | Kalau tidak dipenuhi |
|---|---|
| Cache bersama (Redis) | Batas 60/menit dengan 4 kontainer menjadi 240/menit |
| Trusted proxy dikonfigurasi | Semua permintaan terlihat berasal dari IP load balancer โ satu pengguna berisik mengunci semua orang |
Rate limiting aplikasi bukan pertahanan DDoS. Ia bekerja setelah permintaan sampai ke PHP, jadi serangan volumetrik tetap menghabiskan koneksi dan CPU-mu. Untuk itu, lapisan yang tepat ada di depan: AWS WAF beserta aturan berbasis laju di CloudFront (Fase 9). Rate limiter Laravel menangani penyalahgunaan pada tingkat logika bisnis โ dan itu tetap perlu.
Latihan: pasang throttle:login pada rute loginmu, lalu kirim enam permintaan salah
berturut-turut dengan curl -i. Amati header X-RateLimit-Remaining turun, lalu status
berubah jadi 429 beserta Retry-After.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.