Rate Limiting

Rate Limiting #

Rate limiting membatasi seberapa sering sebuah klien bisa mengirim request ke server dalam periode waktu tertentu. Ini melindungi aplikasi dari brute force login, scraping berlebihan, penggunaan API yang tidak wajar, dan serangan DoS ringan.

Cara Kerja: Leaky Bucket #

Nginx mengimplementasikan rate limiting dengan algoritma leaky bucket. Bayangkan sebuah ember dengan lubang di bawah:

Request masuk → ember (bucket/zone)
                    │
               [     ] ← request yang menunggu (burst)
                    │
                    ▼ request keluar dengan laju yang dikontrol
              ke backend

Request masuk ke “ember” dan keluar dengan laju yang dikontrol. Jika request masuk terlalu cepat:

  • Jika masih ada ruang di ember → request menunggu
  • Jika ember penuh → request ditolak dengan 503 (atau kode yang dikonfigurasi)

Mendefinisikan Zone Rate Limit #

Zone didefinisikan di level http dan digunakan bersama oleh semua worker process:

http {
    # Format: limit_req_zone key zone=nama:ukuran rate=N;
    #
    # key: variabel yang menentukan "siapa" yang dibatasi
    # zone: nama zone dan ukuran shared memory untuk tracking
    # rate: batas request per detik (r/s) atau per menit (r/m)

    # Batasi per IP: 10 request per detik
    limit_req_zone $binary_remote_addr zone=per_ip:10m rate=10r/s;

    # Batasi per IP untuk endpoint login: 5 request per menit
    limit_req_zone $binary_remote_addr zone=login_limit:10m rate=5r/m;

    # Batasi per API key (dari header)
    limit_req_zone $http_x_api_key zone=per_api_key:10m rate=100r/s;
}

$binary_remote_addr (bukan $remote_addr) digunakan karena format biner lebih efisien — menggunakan 4 byte untuk IPv4 dan 16 byte untuk IPv6, dibanding string teks yang lebih panjang.

Ukuran zone 10m bisa menyimpan sekitar 160.000 entry (untuk IPv4). Cukup untuk kebanyakan kasus.


Menggunakan Zone di Location #

server {
    # Terapkan rate limit: tolak request yang melebihi rate
    location / {
        limit_req zone=per_ip;
        proxy_pass http://backend;
    }

    # Dengan burst: izinkan lonjakan singkat hingga N request
    # request yang melampaui rate tapi masih dalam burst → delay
    location /api/ {
        limit_req zone=per_ip burst=20;
        proxy_pass http://api_backend;
    }

    # burst + nodelay: izinkan lonjakan tapi tanpa delay
    # request dalam burst langsung diproses, bukan diqueue
    location /api/search {
        limit_req zone=per_ip burst=50 nodelay;
        proxy_pass http://api_backend;
    }
}

Memahami burst #

rate=10r/s, burst=20:

Skenario: 30 request datang sekaligus

Request 1-10:  langsung diproses (dalam rate normal)
Request 11-30: masuk queue burst (menunggu giliran)
Request 31+:   ditolak 503

Tanpa burst: request 11+ langsung ditolak
Dengan burst: request 11-30 diproses setelah delay
Dengan burst + nodelay: request 11-30 diproses langsung (tanpa delay),
                        tapi "slot burst" terpakai dan perlu diisi ulang

nodelay cocok untuk API yang butuh respons cepat — klien tidak merasakan delay, tapi mereka tetap dibatasi dalam jangka panjang karena slot burst tidak langsung terisi ulang.


Konfigurasi Response saat Limit Terlampaui #

http {
    limit_req_zone $binary_remote_addr zone=per_ip:10m rate=10r/s;

    # Kode status yang dikembalikan saat limit terlampaui (default: 503)
    limit_req_status 429;   # 429 Too Many Requests lebih semantis dari 503

    # Log level untuk pesan rate limit (default: error)
    limit_req_log_level warn;
}

HTTP 429 (Too Many Requests) lebih tepat secara semantis dari 503 (Service Unavailable) untuk rate limiting — dan banyak klien/library HTTP sudah mengenali 429 dan akan melakukan retry dengan backoff otomatis.


Contoh Nyata: Melindungi Endpoint Login #

Endpoint login adalah target utama brute force — perlu rate limiting yang lebih ketat:

http {
    # Rate limit khusus untuk login: 5 percobaan per menit per IP
    limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
    limit_req_status 429;
}

server {
    location = /auth/login {
        limit_req zone=login burst=3 nodelay;

        # Tambahkan header Retry-After agar klien tahu kapan boleh coba lagi
        limit_req_status 429;

        proxy_pass http://auth_backend;
    }

    # API umum dengan rate yang lebih longgar
    location /api/ {
        limit_req zone=per_ip burst=50 nodelay;
        proxy_pass http://api_backend;
    }
}

Rate Limiting Berdasarkan Kombinasi IP dan Path #

Kadang kamu ingin rate limit yang berbeda untuk path yang berbeda, atau kombinasi kondisi:

http {
    # Zone terpisah untuk keperluan berbeda
    limit_req_zone $binary_remote_addr zone=general:10m rate=30r/s;
    limit_req_zone $binary_remote_addr zone=api:10m    rate=10r/s;
    limit_req_zone $binary_remote_addr zone=auth:10m   rate=5r/m;
    limit_req_zone $binary_remote_addr zone=static:10m rate=100r/s;
}

server {
    location /auth/ {
        limit_req zone=auth burst=2;
        proxy_pass http://auth_backend;
    }

    location /api/ {
        limit_req zone=api burst=20 nodelay;
        proxy_pass http://api_backend;
    }

    location ~* \.(css|js|png|jpg)$ {
        limit_req zone=static burst=200 nodelay;
        root /var/www/html;
    }

    location / {
        limit_req zone=general burst=50 nodelay;
        proxy_pass http://backend;
    }
}

Whitelist: Pengecualian dari Rate Limit #

Trafik internal atau monitoring tidak perlu dibatasi:

http {
    limit_req_zone $binary_remote_addr zone=per_ip:10m rate=10r/s;

    # Buat variabel: 0 = kena rate limit, 1 = exempt
    geo $limit {
        default         1;          # Semua kena limit secara default
        10.0.0.0/8      0;          # Jaringan internal dikecualikan
        192.168.0.0/16  0;
        127.0.0.1       0;          # Localhost dikecualikan
    }

    map $limit $limit_key {
        0 "";                        # Kosong = tidak dibatasi
        1 $binary_remote_addr;       # Gunakan IP sebagai key
    }

    limit_req_zone $limit_key zone=per_ip:10m rate=10r/s;
}

Dengan trik ini, request dari jaringan internal menggunakan key kosong (""), yang tidak pernah melampaui limit karena Nginx tidak tracking key kosong.


Ringkasan #

  • limit_req_zone di level http mendefinisikan zone; limit_req zone=nama di location menerapkannya.
  • burst mengizinkan lonjakan request singkat — tanpa nodelay request di-queue, dengan nodelay diproses langsung.
  • Gunakan limit_req_status 429 (bukan default 503) — lebih semantis dan klien HTTP biasanya tahu cara menangani 429.
  • $binary_remote_addr lebih efisien dari $remote_addr untuk key rate limiting.
  • Gunakan geo + map untuk mengecualikan IP tertentu (jaringan internal, monitoring) dari rate limiting.

← Sebelumnya: Basic Auth   Berikutnya: Pembatasan IP →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact