TechTopia Casing SSD NVMe NGFF USB 3.1 Type-C Enclosure Eksternal M.2 SATA & NVMe High Speed

Laravel Rate Limiting: Membatasi Request API untuk Mencegah Abuse

API memungkinkan aplikasi menerima request dari banyak client secara bersamaan. Namun, tanpa pembatasan yang tepat, endpoint API dapat disalahgunakan untuk mengirim request secara berlebihan, melakukan brute force, scraping agresif, spam, atau membebani server.

Laravel menyediakan fitur Rate Limiting untuk mengatur berapa banyak request yang boleh dilakukan dalam periode tertentu. Dengan rate limiting, aplikasi dapat memberikan batas request berdasarkan IP address, user, token, endpoint, atau kombinasi beberapa identifier.

Dalam artikel ini, kita akan membahas cara kerja Laravel Rate Limiting, penggunaan RateLimiter, middleware throttle, custom rate limiter, response HTTP 429, hingga praktik terbaik untuk API Laravel.

Apa Itu Laravel Rate Limiting?

Rate limiting adalah mekanisme untuk membatasi jumlah request yang dapat dilakukan oleh client dalam periode waktu tertentu.

Contohnya, sebuah endpoint API dapat memiliki aturan:

  • Maksimal 60 request per menit.
  • Maksimal 10 request per detik.
  • Maksimal 5 percobaan login per menit.
  • Maksimal 1.000 request per jam untuk sebuah API key.

Jika client melewati batas tersebut, server dapat menolak request sementara dan mengembalikan HTTP status 429 Too Many Requests.

Mengapa Rate Limiting Penting?

Rate limiting bukan hanya fitur keamanan. Dalam aplikasi Laravel, rate limiting juga membantu menjaga stabilitas resource server.

1. Mencegah Abuse

Client yang bermasalah dapat mengirim request dalam jumlah sangat besar. Tanpa rate limiting, endpoint yang sederhana sekalipun dapat menjadi sumber beban tinggi.

2. Mengurangi Brute Force

Endpoint seperti login, OTP, password reset, atau verification code sangat cocok menggunakan rate limiting untuk memperlambat percobaan berulang.

3. Melindungi Database

Setiap request API dapat menjalankan query database. Jika satu client mengirim ribuan request dalam waktu singkat, database dapat menjadi bottleneck.

4. Menjaga Performa Aplikasi

Rate limiting membantu menjaga agar satu client tidak mengambil resource secara tidak proporsional dibandingkan client lainnya.

5. Mengontrol Penggunaan API

Pada aplikasi SaaS atau API publik, rate limiting dapat digunakan untuk membedakan batas penggunaan berdasarkan user, API key, subscription plan, atau jenis endpoint.

Konsep Dasar Rate Limiting

Secara sederhana, rate limiter dapat digambarkan seperti berikut:

Client
   |
   | Request API
   v
+------------------+
|   Rate Limiter   |
+------------------+
        |
        +---- Limit tersedia ----> Controller ----> Response 200
        |
        +---- Limit habis -------> Response 429

Misalnya endpoint memiliki batas 60 request per menit.

Request 1  -> Allowed
Request 2  -> Allowed
Request 3  -> Allowed
...
Request 60 -> Allowed
Request 61 -> 429 Too Many Requests

Setelah periode pembatasan berakhir, client dapat kembali melakukan request sesuai aturan limiter.

Laravel RateLimiter

Laravel menyediakan class Illuminate\Support\Facades\RateLimiter yang dapat digunakan untuk membuat dan memeriksa batas request secara programmatic.

Contoh sederhana:

use Illuminate\Support\Facades\RateLimiter;

$key = 'api:' . request()->ip();

if (RateLimiter::tooManyAttempts($key, 60)) {
    return response()->json([
        'message' => 'Too many requests.'
    ], 429);
}

RateLimiter::hit($key, 60);

return response()->json([
    'message' => 'Request allowed.'
]);

Pada contoh tersebut, aplikasi menggunakan IP address sebagai identifier.

Memahami Key pada Rate Limiting

Rate limiter membutuhkan sebuah key untuk menentukan siapa atau apa yang sedang dibatasi.

Key dapat dibuat berdasarkan:

  • IP address.
  • User ID.
  • Email address.
  • API key.
  • Token.
  • Kombinasi user dan endpoint.

Contoh berdasarkan IP:

$key = 'api:' . request()->ip();

Contoh berdasarkan user yang sedang login:

$key = 'api:user:' . auth()->id();

Contoh kombinasi user dan endpoint:

$key = 'api:' . auth()->id() . ':' . request()->path();

Pemilihan key sangat penting karena menentukan bagaimana limit diterapkan.

Rate Limiting dengan Middleware Throttle

Salah satu cara paling praktis untuk membatasi request API di Laravel adalah menggunakan middleware throttle.

Contohnya:

Route::middleware('throttle:60,1')->group(function () {
    Route::get('/products', [ProductController::class, 'index']);
    Route::get('/orders', [OrderController::class, 'index']);
});

Format:

throttle:max_attempts,decay_minutes

Jadi:

throttle:60,1

berarti maksimal sekitar 60 request dalam periode satu menit untuk limiter tersebut.

Rate Limiting pada Satu Route

Jika hanya satu endpoint yang perlu dibatasi, middleware dapat dipasang langsung pada route.

Route::get('/reports', [ReportController::class, 'index'])
    ->middleware('throttle:30,1');

Endpoint tersebut memiliki aturan rate limiting yang lebih ketat dibandingkan endpoint lainnya.

Rate Limiting pada Route Group

Untuk beberapa endpoint yang memiliki aturan sama, gunakan route group.

Route::middleware('throttle:100,1')->group(function () {
    Route::get('/products', [ProductController::class, 'index']);
    Route::get('/categories', [CategoryController::class, 'index']);
    Route::get('/brands', [BrandController::class, 'index']);
});

Pendekatan ini lebih mudah dipelihara daripada menambahkan konfigurasi yang sama pada setiap route.

Membuat Named Rate Limiter

Untuk aplikasi yang lebih kompleks, sebaiknya gunakan named rate limiter agar aturan pembatasan tidak tersebar di banyak file.

Laravel menyediakan RateLimiter::for() untuk mendefinisikan rate limiter.

Contoh:

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;

RateLimiter::for('api', function ($request) {
    return Limit::perMinute(60);
});

Limiter tersebut kemudian dapat digunakan pada route:

Route::middleware('throttle:api')->group(function () {
    Route::get('/products', [ProductController::class, 'index']);
    Route::get('/orders', [OrderController::class, 'index']);
});

Dengan named limiter, konfigurasi rate limit menjadi lebih terpusat.

Rate Limiting Berdasarkan User

Pada aplikasi yang memiliki authentication, sering kali lebih tepat membatasi request berdasarkan user dibandingkan hanya berdasarkan IP.

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;

RateLimiter::for('api', function ($request) {
    return $request->user()
        ? Limit::perMinute(120)->by($request->user()->id)
        : Limit::perMinute(30)->by($request->ip());
});

Logikanya:

User login
    |
    v
User ID digunakan sebagai key
    |
    v
120 request / minute


Guest
    |
    v
IP address digunakan sebagai key
    |
    v
30 request / minute

Pendekatan ini memungkinkan aplikasi memberikan limit yang berbeda untuk authenticated user dan guest.

Rate Limiting Berdasarkan IP Address

Untuk endpoint publik yang tidak membutuhkan authentication, IP address dapat digunakan sebagai identifier.

RateLimiter::for('public-api', function ($request) {
    return Limit::perMinute(30)->by($request->ip());
});

Contohnya dapat digunakan untuk endpoint pencarian publik, contact form API, atau endpoint informasi yang tidak memerlukan login.

Rate Limiting Berdasarkan API Key

Pada API yang menggunakan API key, identifier dapat menggunakan key tersebut.

RateLimiter::for('partner-api', function ($request) {
    return Limit::perMinute(1000)
        ->by($request->header('X-API-Key'));
});

Dengan pendekatan ini, setiap API client dapat memiliki quota masing-masing.

Contoh konseptual:

API Key A -> 1000 request / minute
API Key B -> 1000 request / minute
API Key C -> 1000 request / minute

Limit Berbeda Berdasarkan Subscription

Rate limiting juga dapat disesuaikan berdasarkan paket pengguna.

RateLimiter::for('api', function ($request) {
    $user = $request->user();

    if (! $user) {
        return Limit::perMinute(30)->by($request->ip());
    }

    return match ($user->plan) {
        'free' => Limit::perMinute(60)->by($user->id),
        'pro' => Limit::perMinute(300)->by($user->id),
        'enterprise' => Limit::perMinute(1000)->by($user->id),
        default => Limit::perMinute(30)->by($user->id),
    };
});

Dengan model seperti ini, rate limit dapat menjadi bagian dari sistem subscription aplikasi.

Multiple Limits

Sebuah endpoint dapat memiliki lebih dari satu aturan pembatasan.

Contohnya, user dapat dibatasi berdasarkan user ID sekaligus IP address.

RateLimiter::for('api', function ($request) {
    return [
        Limit::perMinute(100)->by(
            $request->user()?->id ?? $request->ip()
        ),

        Limit::perMinute(300)->by(
            $request->ip()
        ),
    ];
});

Pendekatan tersebut berguna ketika aplikasi ingin menerapkan beberapa lapisan perlindungan.

Mengapa HTTP 429 Digunakan?

Ketika request melewati rate limit, HTTP status yang umum digunakan adalah:

429 Too Many Requests

Contoh response JSON:

{
    "message": "Too Many Requests"
}

Client kemudian dapat menunggu beberapa saat sebelum melakukan request kembali.

Header Rate Limit

Informasi rate limit dapat membantu client mengetahui kondisi quota mereka.

Secara umum, response API dapat memberikan informasi seperti:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 12
Retry-After: 30

Nama dan format header dapat berbeda tergantung konfigurasi aplikasi, middleware, atau API gateway yang digunakan.

Rate Limiting untuk Login

Endpoint login adalah salah satu target utama brute force.

Misalnya, jangan membiarkan sebuah client mencoba password tanpa batas.

use Illuminate\Support\Facades\RateLimiter;

$key = strtolower($request->email) . '|' . $request->ip();

if (RateLimiter::tooManyAttempts($key, 5)) {
    return response()->json([
        'message' => 'Terlalu banyak percobaan login.'
    ], 429);
}

RateLimiter::hit($key, 60);

Pada contoh tersebut, key menggabungkan email dan IP sehingga percobaan login dapat dibatasi secara lebih spesifik.

Membersihkan Rate Limit

Setelah sebuah proses berhasil, rate limit tertentu dapat dihapus menggunakan RateLimiter::clear().

RateLimiter::clear($key);

Contohnya pada proses login, aplikasi dapat membersihkan counter setelah kredensial berhasil diverifikasi.

if (Auth::attempt($credentials)) {
    RateLimiter::clear($key);

    $request->session()->regenerate();

    return response()->json([
        'message' => 'Login berhasil.'
    ]);
}

Memeriksa Jumlah Percobaan

Laravel menyediakan method untuk mengetahui jumlah attempt yang sudah digunakan.

$attempts = RateLimiter::attempts($key);

Informasi ini dapat digunakan untuk kebutuhan internal aplikasi, logging, atau observability.

Menggunakan AvailableIn

Method availableIn() dapat digunakan untuk mengetahui berapa detik lagi sebelum rate limit tersedia kembali.

$seconds = RateLimiter::availableIn($key);

Nilai tersebut dapat digunakan untuk membuat pesan response yang lebih informatif.

if (RateLimiter::tooManyAttempts($key, 60)) {
    $seconds = RateLimiter::availableIn($key);

    return response()->json([
        'message' => 'Too many requests.',
        'retry_after' => $seconds,
    ], 429);
}

Rate Limiting untuk Endpoint Sensitif

Tidak semua endpoint membutuhkan limit yang sama.

Endpoint yang sebaiknya mendapat perhatian khusus antara lain:

  • Login.
  • Register.
  • Password reset.
  • OTP.
  • Email verification.
  • Search.
  • File upload.
  • Export data.
  • Generate report.
  • Endpoint yang menjalankan query berat.

Endpoint yang melakukan operasi database atau proses CPU-intensive sebaiknya memiliki limit lebih ketat daripada endpoint sederhana.

Rate Limiting dan Endpoint Berat

Misalnya endpoint berikut menghasilkan laporan:

Route::get('/reports/monthly', [
    ReportController::class,
    'monthly'
])->middleware('throttle:10,1');

Limit yang lebih rendah masuk akal apabila setiap request membutuhkan query database besar atau proses agregasi yang mahal.

Rate Limiting Bukan Pengganti Authorization

Rate limiting tidak menggantikan authentication dan authorization.

Ketiganya memiliki fungsi berbeda:

MekanismeFungsi
AuthenticationMenentukan siapa pengguna.
AuthorizationMenentukan apa yang boleh dilakukan pengguna.
Rate LimitingMenentukan seberapa sering request boleh dilakukan.

Contohnya, user yang sudah login tetap tidak boleh mengakses data milik user lain hanya karena request-nya masih berada di bawah rate limit.

Rate Limiting dan Queue

Rate limiting juga perlu dibedakan dari queue.

Rate limiting membatasi frekuensi request.

Queue membantu memindahkan pekerjaan berat ke proses asynchronous.

Keduanya dapat digunakan bersama.

Client
   |
   v
Rate Limiter
   |
   v
API Controller
   |
   v
Dispatch Job
   |
   v
Queue
   |
   v
Worker
   |
   v
Database / External API

Contohnya, endpoint export data dapat dibatasi menjadi 5 request per menit, sementara proses export sebenarnya dijalankan oleh queue worker.

Rate Limiting dan Cache

Rate limiter membutuhkan penyimpanan untuk mencatat counter request. Pada deployment tertentu, aplikasi dapat menggunakan cache backend seperti Redis agar counter dapat diakses secara cepat.

Hal ini menjadi penting ketika aplikasi berjalan pada beberapa server.

                 +----------------+
                 | Load Balancer  |
                 +-------+--------+
                         |
             +-----------+-----------+
             |                       |
             v                       v
       +-----------+           +-----------+
       | Laravel 1 |           | Laravel 2 |
       +-----+-----+           +-----+-----+
             |                       |
             +-----------+-----------+
                         |
                         v
                    +---------+
                    |  Redis  |
                    +---------+

Dengan shared storage, counter rate limiting dapat tetap konsisten meskipun request berpindah-pindah antar server.

Kenapa Rate Limiting Bisa Bermasalah di Multi-Server?

Bayangkan aplikasi memiliki dua server dan masing-masing server menggunakan penyimpanan rate limit lokal.

Client
  |
  +----> Server A -> Counter A
  |
  +----> Server B -> Counter B

Jika limit seharusnya 60 request per menit, client dapat secara efektif memperoleh quota yang lebih besar karena counter terbagi pada beberapa server.

Karena itu, deployment horizontal perlu mempertimbangkan penggunaan shared cache atau storage yang sesuai.

Rate Limiting dengan Redis

Redis sering digunakan sebagai backend cache pada aplikasi Laravel berskala lebih besar karena cepat dan dapat digunakan bersama oleh beberapa instance aplikasi.

Konfigurasi detailnya bergantung pada versi Laravel, driver cache, dan environment deployment.

Setelah Redis digunakan sebagai cache backend, rate limiting yang bergantung pada cache dapat memanfaatkan storage tersebut secara terpusat.

Rate Limiting untuk API Publik

API publik biasanya perlu memiliki aturan yang lebih jelas dibandingkan API internal.

Contoh kebijakan:

ClientLimit
Guest30 request/menit
Free User60 request/menit
Pro User300 request/menit
EnterpriseSesuai kontrak

Angka tersebut hanya contoh. Nilai sebenarnya harus ditentukan berdasarkan kapasitas server, pola penggunaan, dan kebutuhan bisnis.

Rate Limiting untuk Public Search

Endpoint pencarian sering menjadi target abuse karena relatif mudah dipanggil secara otomatis.

Contoh:

Route::get('/search', [SearchController::class, 'index'])
    ->middleware('throttle:30,1');

Selain rate limiting, query pencarian sebaiknya juga memiliki pagination dan batas jumlah data.

$products = Product::query()
    ->where('name', 'like', '%' . $request->search . '%')
    ->paginate(20);

Rate Limiting dan Pagination

Rate limiting tidak boleh menjadi satu-satunya mekanisme untuk mengendalikan penggunaan API.

Endpoint list sebaiknya juga menggunakan pagination.

return Product::query()
    ->paginate(20);

Tanpa pagination, satu request dapat meminta terlalu banyak data sekaligus meskipun jumlah request masih berada di bawah rate limit.

Rate Limiting dan Request Payload

Rate limit juga tidak melindungi aplikasi dari payload yang terlalu besar.

Endpoint upload atau endpoint yang menerima JSON besar membutuhkan pembatasan ukuran request secara terpisah.

Dengan kata lain:

Rate Limit      -> Berapa banyak request
Request Size    -> Seberapa besar setiap request
Pagination      -> Berapa banyak data per response
Authorization   -> Data apa yang boleh diakses

Keempat aspek tersebut sebaiknya dipertimbangkan secara terpisah.

Rate Limiting dan HTTP 429 pada Client

Client API sebaiknya tidak langsung melakukan retry terus-menerus ketika menerima HTTP 429.

Implementasi client yang baik biasanya menggunakan strategi seperti:

  1. Membaca status HTTP 429.
  2. Membaca informasi retry jika tersedia.
  3. Menunggu sebelum mencoba kembali.
  4. Menggunakan exponential backoff jika diperlukan.

Contoh konsep:

Request
   |
   v
HTTP 429
   |
   v
Wait
   |
   v
Retry
   |
   +----> Success
   |
   +----> 429 -> Backoff lagi

Common Mistakes dalam Laravel Rate Limiting

1. Menggunakan Limit Terlalu Tinggi

Limit yang terlalu tinggi dapat membuat rate limiting tidak efektif.

2. Menggunakan Limit Terlalu Rendah

Limit yang terlalu rendah dapat membuat pengguna normal sering mendapatkan HTTP 429.

3. Hanya Mengandalkan IP

IP address tidak selalu mewakili satu user. Banyak pengguna dapat berbagi IP melalui NAT, proxy, kantor, atau jaringan seluler.

4. Tidak Mempertimbangkan Multi-Server

Deployment dengan beberapa instance Laravel perlu memperhatikan storage rate limiter agar counter tidak terpisah.

5. Tidak Memberikan Informasi kepada Client

API client lebih mudah menangani rate limit apabila response memberikan informasi yang jelas mengenai kondisi throttling.

6. Semua Endpoint Menggunakan Limit yang Sama

Endpoint sederhana dan endpoint yang melakukan pekerjaan berat tidak selalu membutuhkan rate limit yang sama.

Best Practice Laravel Rate Limiting

  • Gunakan rate limiting pada endpoint publik.
  • Berikan limit lebih ketat pada endpoint authentication.
  • Gunakan identifier yang sesuai, seperti user ID, IP, atau API key.
  • Gunakan named rate limiter untuk aturan yang kompleks.
  • Gunakan shared cache pada deployment multi-server jika diperlukan.
  • Gunakan pagination untuk endpoint list.
  • Batasi ukuran request dan upload secara terpisah.
  • Gunakan authorization untuk melindungi akses data.
  • Monitor response HTTP 429.
  • Dokumentasikan batas API untuk developer.
  • Jangan menggunakan rate limit sebagai satu-satunya lapisan keamanan.

Contoh Struktur Rate Limiting API Laravel

Berikut contoh pendekatan sederhana untuk aplikasi API:

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;

RateLimiter::for('api', function ($request) {
    if ($request->user()) {
        return Limit::perMinute(120)
            ->by($request->user()->id);
    }

    return Limit::perMinute(30)
        ->by($request->ip());
});

Route kemudian menggunakan limiter tersebut:

Route::middleware('throttle:api')->group(function () {
    Route::get('/products', [ProductController::class, 'index']);
    Route::get('/categories', [CategoryController::class, 'index']);
    Route::post('/orders', [OrderController::class, 'store']);
});

Untuk endpoint yang lebih sensitif, gunakan limit khusus.

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

Contoh Arsitektur API yang Lebih Aman

                    Client
                      |
                      v
              +---------------+
              | Load Balancer |
              +-------+-------+
                      |
                      v
              +---------------+
              | Laravel API   |
              +-------+-------+
                      |
             +--------+--------+
             |                 |
             v                 v
      Authentication     Rate Limiter
             |                 |
             +--------+--------+
                      |
                      v
                 Controller
                      |
              +-------+-------+
              |               |
              v               v
          Database          Queue

Pada arsitektur tersebut, rate limiting menjadi salah satu lapisan proteksi sebelum request menjalankan proses aplikasi yang lebih mahal.

Rate Limiting vs Throttling

Istilah rate limiting dan throttling sering digunakan secara bergantian.

Dalam praktik API, keduanya sama-sama berkaitan dengan pembatasan request. Laravel juga menggunakan istilah throttle pada middleware yang menerapkan mekanisme rate limiting.

Jadi ketika melihat:

->middleware('throttle:60,1')

artinya route tersebut mendapatkan pembatasan request.

Checklist Sebelum Production

  • Apakah endpoint publik sudah memiliki rate limit?
  • Apakah login dan password reset sudah dibatasi?
  • Apakah endpoint berat memiliki limit lebih ketat?
  • Apakah API memiliki response 429 yang sesuai?
  • Apakah client mengetahui cara menangani 429?
  • Apakah pagination sudah diterapkan?
  • Apakah request size dibatasi?
  • Apakah identifier rate limiter sudah tepat?
  • Apakah deployment multi-server menggunakan storage yang sesuai?
  • Apakah response 429 dimonitor?

Ringkasan Laravel Rate Limiting

FiturKegunaan
throttleMembatasi request pada route atau route group.
RateLimiter::for()Membuat named rate limiter.
Limit::perMinute()Mendefinisikan batas request per menit.
Limit::by()Menentukan identifier pembatasan.
tooManyAttempts()Memeriksa apakah batas sudah terlampaui.
hit()Mencatat sebuah attempt.
clear()Menghapus counter limiter.
HTTP 429Response ketika terlalu banyak request.

Kesimpulan

Laravel Rate Limiting merupakan salah satu mekanisme penting untuk menjaga API tetap aman dan stabil. Dengan membatasi jumlah request berdasarkan user, IP address, API key, atau identifier lainnya, aplikasi dapat mengurangi risiko abuse dan beban berlebihan pada server.

Untuk aplikasi sederhana, middleware throttle dapat menjadi solusi yang praktis. Sementara untuk aplikasi yang lebih kompleks, named rate limiter dengan RateLimiter::for() memungkinkan aturan yang lebih fleksibel, termasuk limit berbeda berdasarkan user atau subscription.

Rate limiting sebaiknya tidak berdiri sendiri. Gunakan bersama authentication, authorization, pagination, request validation, queue, monitoring, dan konfigurasi infrastructure yang tepat.

Dengan desain rate limiting yang baik, API Laravel dapat memberikan akses yang terkontrol kepada client sekaligus tetap menjaga resource server dari request berlebihan dan pola penggunaan yang tidak wajar.