Rate limiting backend adalah cara membatasi permintaan yang masuk ke server dalam periode waktu tertentu. Tujuannya melindungi aplikasi dari lonjakan trafik, bot, dan percobaan peretasan. Namun, pembatasan yang asal-asalan justru bisa menolak pengguna sungguhan yang tidak melakukan kesalahan apa pun.
Bayangkan seorang pelanggan sedang membuka dashboard. Halaman itu mengirim belasan permintaan sekaligus, lalu tiba-tiba muncul pesan error. Pelanggan mengira aplikasi Anda rusak, padahal server hanya menjalankan aturan yang terlalu kaku. Karena itu, tantangan sebenarnya bukan sekadar memblokir, melainkan memblokir dengan cerdas.
Artikel ini membahas apa itu rate limiting backend, alasan pembatasan bisa mengganggu pengguna sah, lima prinsip yang lebih ramah, dan contoh kode di Express. Selain itu, Anda akan menemukan kesalahan umum dan daftar periksa sebelum rilis.
Daftar Isi
- Apa itu rate limiting backend
- Mengapa membatasi permintaan bisa mengganggu pengguna sah
- 5 prinsip rate limiting backend yang ramah pengguna
- Contoh cara membatasi permintaan di Express
- Rate limiting backend pada skala besar
- Kesalahan umum saat membatasi permintaan
- Daftar periksa sebelum rilis
- Kesimpulan
Apa Itu Rate Limiting Backend dan Mengapa Dibutuhkan?
Rate limiting backend adalah mekanisme di sisi server yang menghitung jumlah permintaan dari satu klien, lalu menolak permintaan berlebih. Server biasanya menjawab dengan status HTTP 429. Menurut dokumentasi MDN tentang status 429, respons ini menandakan klien mengirim terlalu banyak permintaan dalam waktu tertentu, dan server boleh menyertakan header Retry-After.
Ada beberapa alasan mengapa fitur ini penting:
- Melindungi server. Satu klien yang agresif tidak boleh menghabiskan sumber daya untuk semua orang.
- Menahan serangan tebak kata sandi. Endpoint login yang dibiarkan terbuka mudah diserang berulang kali.
- Mengurangi scraping dan bot. Pengambilan data massal membebani server tanpa memberi nilai bagi pengguna.
- Menahan dampak bug klien. Kode front-end yang masuk ke perulangan tak terbatas bisa membanjiri API Anda sendiri.
- Mengendalikan biaya. Setiap permintaan memakai CPU, database, atau layanan pihak ketiga yang berbayar.
Mengapa Membatasi Permintaan Bisa Mengganggu Pengguna Sah?
Aturan yang tampak aman di atas kertas sering bermasalah di dunia nyata. Pertama, banyak pengguna berbagi satu alamat IP publik, misalnya di kantor, kampus, atau jaringan seluler. Jika Anda membatasi hanya berdasarkan IP, satu orang yang agresif bisa memblokir ratusan orang lain.
Kedua, satu halaman web sering memuat banyak data sekaligus. Batas yang terlalu kecil membuat halaman gagal tampil walaupun pengguna hanya membukanya satu kali. Ketiga, lonjakan wajar bisa terjadi saat promo atau peluncuran fitur. Aturan yang tidak fleksibel akan menganggap lonjakan itu sebagai serangan.
Terakhir, pesan error yang membingungkan memperburuk keadaan. Pengguna tidak tahu apakah mereka salah, aplikasinya rusak, atau koneksinya bermasalah. Akibatnya, mereka menekan tombol berulang kali dan memperparah beban server.
Baca juga: Rate Limiting API: 4 Algoritma dan Cara Pakainya di Express
5 Prinsip Rate Limiting Backend yang Ramah Pengguna
Kelima prinsip berikut membantu Anda membatasi permintaan tanpa menghukum pengguna yang tidak bersalah.
1. Kenali Pengguna, Bukan Hanya Alamat IP
Gunakan identitas yang paling spesifik untuk menyusun kunci pembatasan. Untuk pengguna yang sudah masuk, pakai ID pengguna. Untuk klien API, pakai API key. Baru jika keduanya tidak ada, gunakan alamat IP sebagai cadangan.
Selain itu, perhatikan konfigurasi proxy. Jika aplikasi berjalan di belakang reverse proxy atau load balancer, Express perlu pengaturan trust proxy agar req.ip menunjukkan alamat klien asli. Tanpa pengaturan itu, semua pengguna akan terlihat berasal dari alamat proxy yang sama. Identitas sesi yang berasal dari cookie juga bisa dipakai, dan perbedaannya dengan penyimpanan lain dijelaskan di Local Storage, Session Storage, Cookies: 7 Perbedaan.
2. Bedakan Batas untuk Setiap Endpoint
Tidak semua endpoint memiliki risiko yang sama. Endpoint login atau pengiriman kode OTP perlu batas ketat. Sebaliknya, endpoint untuk membaca data publik bisa lebih longgar. Tabel berikut memberi contoh angka awal, yang harus Anda sesuaikan dengan data trafik sebenarnya.
| Endpoint | Contoh batas awal | Alasan |
|---|---|---|
| Login | 10 percobaan per 15 menit | Menahan tebak kata sandi |
| Kirim OTP atau reset sandi | 3 sampai 5 per jam | Mencegah penyalahgunaan dan biaya pesan |
| Pencarian | 30 per menit | Query pencarian cukup mahal |
| Baca data umum | 120 per menit | Ringan dan sering dipakai |
| Unggah berkas | Ketat, per ukuran dan jumlah | Memakai bandwidth dan penyimpanan |
Jika Anda memakai GraphQL, satu permintaan bisa sangat berat. Karena itu, batasi berdasarkan kompleksitas query, bukan hanya jumlah permintaan. Perbandingan kedua pendekatan ada di REST API vs GraphQL: Mana yang Lebih Cocok untuk Proyek Web Modern?.
3. Izinkan Lonjakan Singkat
Pengguna manusia tidak mengirim permintaan dengan ritme yang rapi. Mereka membuka halaman, lalu beberapa permintaan muncul bersamaan. Setelah itu, mereka diam beberapa detik. Pola ini wajar dan seharusnya tidak ditolak.
Algoritma seperti token bucket dan sliding window mampu menampung lonjakan singkat sambil tetap menjaga rata-rata jangka panjang. Penjelasan setiap algoritma ada di artikel Rate Limiting API: 4 Algoritma dan Cara Pakainya di Express. Di sini, cukup ingat satu hal: pilih algoritma yang cocok dengan perilaku pengguna Anda.
4. Beri Tahu Pengguna dengan Respons 429 yang Jelas
Saat menolak permintaan, kirim informasi yang berguna. Gunakan status 429, bukan 403 atau 500, karena maknanya paling tepat. Sertakan header Retry-After supaya klien tahu kapan boleh mencoba lagi. Selain itu, sertakan pesan yang mudah dibaca manusia.
Banyak API juga mengirim header sisa kuota, seperti RateLimit-Limit, RateLimit-Remaining, dan RateLimit-Reset. Nama header ini berasal dari draf standar IETF yang masih berkembang, sedangkan sebagian layanan memakai awalan X-RateLimit-. Pilih satu format, lalu dokumentasikan secara konsisten.
5. Siapkan Klien agar Mundur dengan Sopan
Rate limiting bekerja paling baik jika klien ikut bekerja sama. Klien perlu membaca Retry-After, menunggu, lalu mencoba lagi dengan jeda yang bertambah. Teknik ini disebut exponential backoff. Contoh berikut menunjukkan versi sederhananya di JavaScript.
async function fetchWithRetry(url, options = {}, maxRetry = 3) {
for (let attempt = 0; attempt <= maxRetry; attempt++) {
const res = await fetch(url, options);
if (res.status !== 429) return res;
const retryAfter = Number(res.headers.get('Retry-After'));
const waitSeconds = retryAfter || 2 ** attempt;
const jitter = Math.random() * 500;
await new Promise((resolve) =>
setTimeout(resolve, waitSeconds * 1000 + jitter)
);
}
throw new Error('Terlalu banyak permintaan. Coba lagi nanti.');
}
Variabel jitter menambahkan jeda acak kecil. Dengan begitu, ribuan klien tidak mencoba ulang pada detik yang sama. Di antarmuka, tampilkan pesan seperti “Coba lagi dalam 30 detik” dan nonaktifkan tombol sementara. Teknik sisi klien seperti debounce dan throttle juga mengurangi permintaan yang tidak perlu, seperti dibahas di Debounce vs Throttle JavaScript: Perbedaan dan Contoh Mudah. Namun, teknik itu tidak menggantikan rate limiting backend, karena klien selalu bisa dilewati.

Contoh Cara Membatasi Permintaan di Express
Kode berikut adalah middleware sederhana untuk memperlihatkan idenya. Middleware ini memakai penghitung jendela waktu tetap yang disimpan di memori. Tujuannya mengilustrasikan kunci per pengguna, header, dan respons 429, bukan menjadi solusi produksi.
function rateLimit({ windowMs, limit, keyFn }) {
const buckets = new Map();
// Bersihkan data kedaluwarsa agar memori tidak membengkak
setInterval(() => {
const now = Date.now();
for (const [key, entry] of buckets) {
if (now >= entry.resetAt) buckets.delete(key);
}
}, 60_000).unref();
return (req, res, next) => {
const key = keyFn(req);
const now = Date.now();
let entry = buckets.get(key);
if (!entry || now >= entry.resetAt) {
entry = { count: 0, resetAt: now + windowMs };
buckets.set(key, entry);
}
entry.count += 1;
const remaining = Math.max(0, limit - entry.count);
const resetSeconds = Math.ceil((entry.resetAt - now) / 1000);
res.set({
'RateLimit-Limit': String(limit),
'RateLimit-Remaining': String(remaining),
'RateLimit-Reset': String(resetSeconds),
});
if (entry.count > limit) {
res.set('Retry-After', String(resetSeconds));
return res.status(429).json({
error: 'terlalu_banyak_permintaan',
message: 'Anda terlalu sering mengirim permintaan. Coba lagi sebentar lagi.',
retryAfterSeconds: resetSeconds,
});
}
next();
};
}
Setelah middleware siap, pasang dengan batas yang berbeda untuk tiap kebutuhan. Pastikan express.json() sudah terpasang sebelum memakai req.body pada kunci login.
const express = require('express');
const app = express();
app.set('trust proxy', 1); // sesuaikan dengan jumlah proxy di depan aplikasi
app.use(express.json());
// Prioritaskan ID pengguna, lalu pakai IP sebagai cadangan
const byUserOrIp = (req) => req.user?.id ?? req.ip;
// Batas umum untuk seluruh API
app.use('/api', rateLimit({
windowMs: 60_000,
limit: 120,
keyFn: byUserOrIp,
}));
// Batas lebih ketat untuk login, per kombinasi IP dan email
app.post('/api/login', rateLimit({
windowMs: 15 * 60_000,
limit: 10,
keyFn: (req) => `login:${req.ip}:${req.body?.email ?? ''}`,
}), loginHandler);
Angka batas sebaiknya tidak ditulis mati di dalam kode. Simpan nilai tersebut sebagai konfigurasi, misalnya lewat file .env, sehingga Anda bisa menyesuaikannya tanpa mengubah kode. Panduannya ada di Environment Variable: Cara Pakai .env di Node.js.
Rate Limiting Backend pada Skala Besar: Kapan Perlu Redis?
Penghitung di memori hanya cocok untuk satu proses. Jika aplikasi berjalan di beberapa instance, setiap instance menghitung sendiri-sendiri. Akibatnya, batas efektif menjadi batas per instance dikalikan jumlah instance. Selain itu, penghitung hilang setiap kali aplikasi restart.
Solusinya adalah penyimpanan bersama, dan Redis menjadi pilihan populer karena mendukung operasi atomik dan masa berlaku data. Cara lain adalah memindahkan pembatasan ke lapisan di depan aplikasi, seperti reverse proxy atau API gateway. Namun, aturan yang bergantung pada identitas pengguna biasanya tetap lebih mudah ditangani di aplikasi.
Apa pun pilihan Anda, pantau hasilnya. Persentase respons 429 yang tinggi pada pengguna sah menandakan batas terlalu ketat. Sebaliknya, lonjakan trafik tanpa satu pun respons 429 bisa berarti batas terlalu longgar.
Kesalahan Umum saat Membatasi Permintaan
- Hanya memakai IP. Pengguna di jaringan yang sama saling menghambat.
- Lupa pengaturan proxy. Semua pengguna tampak berasal dari satu alamat yang sama.
- Tanpa Retry-After. Klien tidak tahu kapan boleh mencoba lagi, sehingga terus mengetuk server.
- Pesan error yang kabur. Pengguna mengira aplikasi rusak.
- Memblokir permanen. Pengguna sah yang sekali terkena batas bisa terkunci selamanya.
- Tidak memantau data. Batas ditentukan sekali, lalu tidak pernah ditinjau.
- Mengandalkan klien saja. Pembatasan di front-end mudah dilewati.

Daftar Periksa Rate Limiting Backend sebelum Rilis
- Apakah kunci pembatasan memakai ID pengguna atau API key sebelum IP?
- Apakah pengaturan proxy sudah benar sehingga IP klien terbaca akurat?
- Apakah endpoint sensitif punya batas yang lebih ketat?
- Apakah lonjakan singkat yang wajar masih diizinkan?
- Apakah respons 429 menyertakan Retry-After dan pesan yang jelas?
- Apakah klien membaca pesan itu dan menunggu dengan backoff?
- Apakah penyimpanan penghitung sudah sesuai jumlah instance?
- Apakah respons 429 dipantau dan batasnya bisa diubah lewat konfigurasi?
Kesimpulan: Rate Limiting Backend yang Baik Melindungi Server dan Pengguna
Rate limiting backend bukan sekadar tembok yang menolak semua permintaan berlebih. Pembatasan yang baik mengenali pengguna, membedakan endpoint, menerima lonjakan wajar, dan menjelaskan alasan penolakan dengan jelas. Dengan begitu, Anda tetap melindungi server tanpa membuat pengguna sah merasa dihukum.
Mulailah dari langkah kecil. Pasang batas umum, perketat endpoint sensitif, lalu pantau respons 429. Setelah itu, sesuaikan angka berdasarkan data nyata. Cara membatasi permintaan yang tepat akan terus berkembang seiring pertumbuhan aplikasi Anda.
Artikel Lainnya di NgodingDaily
Lanjutkan belajar dengan topik lain yang saling melengkapi:
- Rate Limiting API: 4 Algoritma dan Cara Pakainya di Express
- Debounce vs Throttle JavaScript: Perbedaan dan Contoh Mudah
- Environment Variable: Cara Pakai .env di Node.js
- REST API vs GraphQL: Mana yang Lebih Cocok untuk Proyek Web Modern?
- Local Storage, Session Storage, Cookies: 7 Perbedaan