Belajar ExpressJS - Rate Limiting & DoS Protection
Episode 20 of 28

Belajar ExpressJS - Rate Limiting & DoS Protection

Melindungi API dari brute-force dan denial-of-service: express-rate-limit dengan store Redis untuk distribusi, express-slow-down untuk throttling, proteksi endpoint login, dan strategi lapisan pertahanan.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Setelah di episode 19 kalian mengelola kerentanan dependency, episode 20 membahas serangan yang menyerang ketersediaan: brute-force dan denial-of-service. Autentikasi yang kuat (episode 12) tidak ada artinya jika penyerang bisa mencoba jutaan password, dan server yang sehat tidak berguna jika bisa dibanjiri request.

Mengapa rate limiting ada di lapisan aplikasi, bukan hanya di firewall/CDN? Karena kontrol terbaik adalah yang dekat dengan logika: rate limit per akun hanya bisa dipahami aplikasi, bukan infrastruktur. Di episode ini kalian membangun dua lapisan — express-rate-limit untuk batas tegas dan express-slow-down untuk pelambatan halus.

express-rate-limit

Middleware Dasar

Install express-rate-limit
npm install express-rate-limit
JSRate limiter global
import { rateLimit } from "express-rate-limit"
 
export const globalLimiter = rateLimit({
  windowMs: 60 * 1000,
  limit: 100,
  standardHeaders: "draft-7",
  legacyHeaders: false,
  message: { error: "TOO_MANY_REQUESTS", message: "Terlalu banyak request" },
})

Middleware ini menghitung request per IP dalam jendela 60 detik; request ke-101 ditolak dengan status 429. standardHeaders: "draft-7" mengirim header RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset yang bisa dibaca client.

Memilih Batas per Route

Batas global melindungi keseluruhan; route sensitif butuh batas lebih ketat:

JSRate limiter khusus auth
export const authLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  limit: 20,
  standardHeaders: "draft-7",
  legacyHeaders: false,
  message: { error: "TOO_MANY_ATTEMPTS", message: "Terlalu banyak percobaan login" },
})
JSPasang di route login
import { authLimiter } from "../middleware/rateLimit.js"
 
router.post("/auth/login", authLimiter, login)

20 percobaan login per 15 menit per IP memperlambat brute-force drastis — coba password bisa jutaan per jam hanya jika tidak dibatasi. Batas ini menutup endpoint yang paling sering diserang.

Tip

Untuk brute-force yang lebih presisi, batasi per akun, bukan hanya per IP — IP bisa diganti penyerang (banyak proxy), akun sulit. Kombinasikan: rate limit ketat per akun + rate limit longgar per IP sebagai jaring pengaman. Ini pola yang sama dengan pembahasan akun vs IP di series autentikasi.

Store Redis: Konsisten di Semua Instance

Rate limiter default menyimpan counter di memori proses — satu instance masing-masing punya hitungan sendiri. Saat aplikasi diskala ke banyak instance (episode 25), penyerang bisa membagi request ke banyak instance dan melewati batas. Solusinya: store bersama di Redis:

Install Redis client
npm install @rate-limit/redis ioredis
JSRate limiter dengan Redis store
import { RedisStore } from "@rate-limit/redis"
import { Redis } from "ioredis"
import { rateLimit } from "express-rate-limit"
import { config } from "../config.js"
 
const client = new Redis(config.redisUrl)
 
export const globalLimiter = rateLimit({
  windowMs: 60 * 1000,
  limit: 100,
  standardHeaders: "draft-7",
  legacyHeaders: false,
  store: new RedisStore({ sendCommand: (...args) => client.sendCommand(args) }),
})

Sekarang hitungan rate limit dibagikan antar instance — satu counter global di Redis, tidak peduli instance mana yang melayani request. Tanpa store bersama, scaling justru melemahkan proteksi.

express-slow-down: Throttle Halus

Kadang menolak (429) bukan perilaku terbaik — user sah yang sedikit melampaui batas akan frustrasi. express-slow-down memperlambat respons alih-alih menolak:

Install express-slow-down
npm install express-slow-down
JSSlow down untuk route umum
import { slowDown } from "express-slow-down"
 
export const apiSlowDown = slowDown({
  windowMs: 60 * 1000,
  delayAfter: 30,
  delayMs: (hits) => Math.min((hits - 30) * 100, 1000),
})

Setelah 30 request dalam satu menit, setiap request berikutnya ditunda — semakin banyak hits, semakin lama delay (hingga 1 detik). Penyerang otomatis melambat; user manusia tidak merasakan apa-apa. Kombinasi ideal: slowDown untuk seluruh API, rateLimit ketat untuk login dan aksi mahal.

Melindungi dari DoS Awal

Batasi Ukuran Body

Rate limit tidak menolong saat satu request membawa body 2 GB. Batasi ukuran sejak parser (episode 5):

JSBatasan ukuran body
app.use(express.json({ limit: "1mb" }))
app.use(express.urlencoded({ extended: true, limit: "1mb" }))

Batasi Upload (episode 15)

JSBatasan ukuran upload
multer({ limits: { fileSize: 5 * 1024 * 1024 } })

Payload Validation Berat ke Queue (episode 22)

Aksi yang butuh komputasi berat sebaiknya tidak memblokir thread utama — dipindah ke background job.

Lapisan Pertahanan Lengkap

100%

Kontrol di level edge (CDN/WAF) menahan volume besar; lapisan aplikasi menangkap pola yang butuh konteks bisnis. Keduanya bekerja bersama — tidak ada satu kontrol yang menangani semuanya.

Common Pitfalls

Rate Limit Hanya di Satu Instance

Tanpa store Redis, setiap instance punya hitungan sendiri — scaling mengalikan batas efektif. Pasang store bersama sebelum scaling.

429 Tanpa Pesan Jelas

Client bingung saat menerima 429 kosong. Kirim bentuk { error, message } (konsisten dengan episode 7) dan header Retry-After agar client tahu kapan boleh mencoba lagi.

Trust Proxy Tidak Diatur

Tanpa trust proxy, semua request terlihat berasal dari IP proxy/load balancer — satu IP yang sama, sehingga semua user kena limit. Atur app.set("trust proxy", 1) saat di belakang proxy (episode 25).

Warning

Batas yang terlalu rendah pada route umum bisa mengunci user sah (misal NAT satu kantor berbagi IP). Pilih nilai berdasarkan data nyata — ukur traffic normal dengan logging episode 17, lalu tetapkan batas dengan margin. Rate limiting melindungi; rate limiting yang salah justru membuat aplikasi kalian ditolak pengguna sendiri.

Penutup

Episode 20 menambah lapisan ketersediaan: express-rate-limit dengan Redis store untuk batas global dan per-route, express-slow-down untuk pelambatan halus, pembatasan ukuran body/upload, dan peta lapisan pertahanan dari CDN hingga handler.

Inti yang harus dibawa pulang:

  • express-rate-limit menolak dengan 429; pasang batas ketat di login, longgar di route umum.
  • Store Redis membuat counter konsisten lintas instance — wajib sebelum scaling.
  • express-slow-down menunda alih-alih menolak — lebih ramah user sah.
  • Batasi ukuran body (limit: "1mb") dan upload (fileSize).
  • Atur trust proxy agar rate limit membaca IP client yang benar.
  • Gabungkan kontrol edge (CDN/WAF) dengan kontrol aplikasi.

Di episode 21 selanjutnya kita akan mengoptimalkan performansi: caching & performance — Redis cache, HTTP caching headers, ETag, dan gzip/compression. Sampai jumpa di episode 21!

Belajar ExpressJS - Rate Limiting & DoS Protection | Belajar ExpressJS