Buffer & Timeout

Buffer & Timeout #

Buffer dan timeout adalah dua aspek reverse proxy yang paling sering diabaikan — tapi dampaknya bisa sangat terasa. Buffering yang salah dikonfigurasi mempengaruhi performa saat backend lambat atau klien memakai koneksi lambat. Timeout yang terlalu pendek menyebabkan error 504 untuk operasi yang seharusnya berhasil; terlalu panjang membiarkan koneksi hang terlalu lama dan menguras resource.

Artikel ini membahas keduanya secara mendalam sampai kita bisa melakukan tuning yang tepat untuk berbagai jenis workload.

Cara Kerja Buffering: Dua Mode #

Ketika Nginx menerima response dari backend, ia bisa bekerja dalam dua mode yang fundamentally berbeda.

Mode 1: Buffering Aktif (Default) #

Backend ──────► Nginx Buffer (RAM/disk) ──────► Klien
          cepat                           lambat OK

Nginx membaca seluruh response dari backend ke buffer memori terlebih dulu. Setelah backend mengirim semua data, Nginx mengurus pengiriman ke klien secara independen — bahkan jika klien sangat lambat mengunduh.

Keuntungan:

  • Backend bebas lebih cepat — bisa menangani request lain
  • Nginx optimal mengelola koneksi klien yang lambat
  • Lebih efisien untuk aplikasi yang mengembalikan banyak response kecil-kecil

Kapan gunakan: Hampir semua web application biasa — REST API, website, dashboard.

Mode 2: Buffering Nonaktif #

Backend ──────► Nginx ──────► Klien
          real-time      real-time

Nginx langsung meneruskan data dari backend ke klien secara real-time, byte per byte. Backend dan klien terhubung secara sinkron.

Keuntungan:

  • Latensi minimal — data sampai ke klien segera
  • Tepat untuk streaming dan event-driven responses

Kapan gunakan: Server-Sent Events (SSE), video streaming, log streaming, long-polling.

flowchart LR
    subgraph BUFFER["Mode: Buffering Aktif"]
        direction LR
        B1["Backend"] -- "kirim cepat" --> N1["Nginx Buffer\n(RAM/disk)"]
        N1 -- "kirim sesuai\nkecepatan klien" --> C1["Klien\n(lambat OK)"]
    end

    subgraph NOBUFFER["Mode: No Buffering"]
        direction LR
        B2["Backend"] -- "sync" --> N2["Nginx\n(hanya relay)"]
        N2 -- "real-time" --> C2["Klien\n(SSE/streaming)"]
    end

    style BUFFER fill:none,stroke-width:2px
    style NOBUFFER fill:none,stroke-width:2px

Directive Buffering #

Konfigurasi Buffer Standar #

http {
    # Aktifkan/nonaktifkan buffering response (default: on)
    proxy_buffering on;

    # Ukuran buffer untuk header response (dan sedikit awal body)
    # Naikkan jika backend mengirim banyak header
    proxy_buffer_size 4k;

    # Jumlah dan ukuran buffer untuk body response
    # Total buffer = 16 × 4k = 64k per koneksi
    # Naikkan jika backend mengirim response besar
    proxy_buffers 16 4k;

    # Batas data yang sudah siap dikirim ke klien (di-buffer tapi belum terkirim)
    # Harus ≥ proxy_buffer_size dan ≤ total proxy_buffers
    proxy_busy_buffers_size 8k;

    # Jika response melebihi semua buffer (di atas), simpan sementara ke file
    # 0 = nonaktifkan file temp (response error jika buffer penuh)
    proxy_max_temp_file_size 1024m;

    # Direktori untuk file temp
    proxy_temp_path /var/cache/nginx/temp 1 2;
}

Tuning Buffer untuk Response Besar #

Untuk API yang mengembalikan JSON besar (misalnya export data):

location /api/export/ {
    proxy_pass http://backend;

    # Naikkan buffer untuk response besar
    proxy_buffer_size       16k;
    proxy_buffers           8 16k;   # Total 128k
    proxy_busy_buffers_size 32k;

    # Izinkan file temp untuk response yang sangat besar
    proxy_max_temp_file_size 500m;
}

Nonaktifkan Buffering untuk Streaming dan SSE #

# Server-Sent Events
location /events {
    proxy_pass http://sse_backend;

    # Nonaktifkan buffering — langsung teruskan ke klien
    proxy_buffering off;
    proxy_cache off;

    # HTTP/1.1 untuk chunked transfer
    proxy_http_version 1.1;
    proxy_set_header Connection '';
    chunked_transfer_encoding on;

    # Timeout panjang — koneksi SSE bisa bertahan lama
    proxy_read_timeout 3600s;
    proxy_send_timeout 3600s;
}

# Video streaming
location /stream/ {
    proxy_pass http://streaming_backend;
    proxy_buffering off;
    proxy_cache off;
    proxy_read_timeout 3600s;

    # Tidak perlu log akses untuk streaming
    access_log off;
}

Lima Jenis Timeout — Perbedaan yang Kritis #

Nginx memiliki beberapa timeout berbeda untuk koneksi ke backend. Memahami apa yang diukur oleh masing-masing adalah kunci untuk men-debug error 502/504.

sequenceDiagram
    participant N as Nginx
    participant B as Backend

    Note over N,B: proxy_connect_timeout
    N->>B: Buka koneksi TCP
    Note over N,B: Jika tidak tersambung dalam batas waktu → 502

    Note over N,B: proxy_send_timeout
    N->>B: Kirim request headers
    N->>B: Kirim request body
    Note over N,B: Idle antara dua write? → 504

    Note over N,B: proxy_read_timeout
    B->>N: Response header
    B->>N: Response body (chunk 1)
    Note over N: Menunggu chunk berikutnya
    B->>N: Response body (chunk 2)
    Note over N,B: Idle antara dua paket? → 504

proxy_connect_timeout #

Waktu maksimum untuk membuka koneksi ke backend. Jika backend tidak merespons TCP handshake dalam waktu ini, Nginx mengembalikan 502.

# Default: 60s — biasanya terlalu lama
proxy_connect_timeout 10s;

# Kapan ini terpicu:
# - Backend tidak berjalan
# - Firewall memblokir port
# - Backend overload dan tidak bisa accept koneksi baru

proxy_send_timeout #

Waktu idle maksimum antara dua write ke backend. Bukan total waktu untuk mengirim request — ini adalah timeout per “paket”. Selama Nginx terus mengirim data, timeout tidak terpicu.

proxy_send_timeout 60s;

# Kapan perlu dinaikkan:
# - Request dengan body sangat besar (file upload)
# - Koneksi ke backend lambat

proxy_read_timeout #

Waktu idle maksimum antara dua paket yang diterima dari backend. Ini yang paling sering salah dipahami — bukan total waktu backend memproses request.

proxy_read_timeout 60s;

# Artinya:
# - Backend BOLEH memproses selama apapun
# - Selama ia sesekali mengirim data (meski hanya 1 byte)
# - Timeout hanya terpicu jika backend DIAM selama 60s

# Contoh yang valid:
# Backend proses laporan besar selama 10 menit, lalu kirim semua sekaligus
# → proxy_read_timeout 600s diperlukan

# Kapan terpicu:
# - Backend hang/deadlock
# - Query database berjalan sangat lama
# - Backend overwhelmed dan tidak merespons

Timeout untuk Sisi Klien #

server {
    # Timeout untuk membaca body request dari klien
    # (antara dua paket yang diterima dari klien)
    client_body_timeout 30s;

    # Timeout untuk membaca header request dari klien
    client_header_timeout 30s;

    # Berapa lama koneksi keep-alive dibiarkan idle sebelum ditutup
    keepalive_timeout 75s;

    # Timeout untuk mengirim response ke klien
    send_timeout 60s;
}

Nilai Timeout yang Tepat untuk Berbagai Skenario #

REST API Biasa #

location /api/ {
    proxy_pass http://api_backend;

    proxy_connect_timeout  5s;    # Backend harus merespons cepat
    proxy_read_timeout    30s;    # API response biasanya < 5 detik
    proxy_send_timeout    30s;
}

Operasi Berat (Laporan, Export, ETL) #

location /api/reports/ {
    proxy_pass http://backend;

    proxy_connect_timeout   5s;
    proxy_read_timeout    300s;   # 5 menit untuk generate laporan besar
    proxy_send_timeout    300s;

    # Buffer besar untuk response export
    proxy_buffer_size       64k;
    proxy_buffers           8 64k;
    proxy_max_temp_file_size 500m;
}

File Upload #

location /upload/ {
    proxy_pass http://backend;

    # Upload file besar butuh waktu kirim yang lama
    proxy_connect_timeout   10s;
    proxy_send_timeout     300s;   # 5 menit untuk upload file besar
    proxy_read_timeout      60s;

    # Ukuran body request yang diizinkan
    client_max_body_size    500m;

    # Buffer body request sebelum diteruskan ke backend
    client_body_buffer_size  10m;
}

WebSocket dan Long-Polling #

location /ws/ {
    proxy_pass http://ws_backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade    $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    proxy_connect_timeout   10s;
    proxy_read_timeout    3600s;   # 1 jam untuk koneksi long-lived
    proxy_send_timeout    3600s;

    # Nonaktifkan buffering untuk WebSocket
    proxy_buffering off;
}

Keepalive Pool ke Backend #

Setiap kali Nginx membuat koneksi baru ke backend, ada overhead TCP handshake (3-way handshake) yang memakan waktu. Dengan keepalive, Nginx mempertahankan pool koneksi yang sudah terbuka dan bisa digunakan kembali untuk request berikutnya.

upstream backend {
    server localhost:3000;

    # Pertahankan maksimum 32 koneksi idle ke backend ini
    # Koneksi yang melebihi 32 akan ditutup setelah request selesai
    keepalive 32;

    # Berapa lama koneksi keepalive dipertahankan
    keepalive_timeout 60s;

    # Berapa banyak request per koneksi sebelum tutup
    keepalive_requests 1000;
}

server {
    location / {
        proxy_pass         http://backend;
        proxy_http_version 1.1;          # Keepalive butuh HTTP/1.1
        proxy_set_header   Connection ""; # Hapus header Connection dari klien
    }
}

Dampak Keepalive #

Tanpa keepalive (setiap request):
  TCP Handshake (1 RTT) → Request → Response → TCP Close
  Total: ~2-3 RTT overhead per request

Dengan keepalive (reuse koneksi):
  Request → Response (koneksi sudah ada, tidak perlu handshake)
  Total: overhead hampir 0

Untuk aplikasi dengan 1000 request/detik ke backend lokal,
keepalive bisa menghemat ribuan handshake per detik.

Membaca dan Men-debug Error yang Terkait Buffer/Timeout #

Kode ErrorPenyebabCara Debug
502 Bad GatewayBackend tidak bisa dihubungi, koneksi ditolak, atau proxy_connect_timeout terlampauiCek apakah backend berjalan: curl localhost:3000
504 Gateway TimeoutBackend terlalu lama merespons — proxy_read_timeout terlampauiNaikkan proxy_read_timeout atau optimalkan backend
413 Request Entity Too LargeBody request melebihi client_max_body_sizeNaikkan client_max_body_size untuk endpoint upload
499 Client Closed RequestKlien memutus koneksi sebelum backend selesaiKlien timeout di sisinya, atau koneksi tidak stabil
# Cara debug 502/504:

# 1. Cek apakah backend berjalan
curl -v http://localhost:3000/api/health

# 2. Cek error log Nginx
tail -f /var/log/nginx/error.log | grep -E "502|504|timeout|connect"

# 3. Ukur waktu response backend langsung
time curl http://localhost:3000/api/heavy-endpoint
# Jika > proxy_read_timeout, maka akan timeout

# 4. Cek apakah ini masalah load (backend responsif saat sendiri tapi tidak saat traffic tinggi)
ab -n 100 -c 10 http://localhost:3000/api/test

# 5. Tambahkan log $upstream_response_time untuk melihat berapa lama backend merespons
log_format detailed '$remote_addr [$time_local] "$request" '
                    '$status $upstream_status '
                    '$upstream_response_time '  # waktu backend
                    '$request_time';            # total waktu termasuk send ke klien

Konfigurasi Buffer & Timeout Lengkap #

http {
    # ─── Default untuk semua proxy ────────────────────────────────────────────
    proxy_buffering         on;
    proxy_buffer_size       4k;
    proxy_buffers           16 4k;
    proxy_busy_buffers_size 8k;
    proxy_max_temp_file_size 1024m;
    proxy_temp_path         /var/cache/nginx/temp 1 2;

    proxy_connect_timeout   10s;
    proxy_read_timeout      60s;
    proxy_send_timeout      60s;

    # ─── Client timeouts ──────────────────────────────────────────────────────
    client_body_timeout     30s;
    client_header_timeout   30s;
    keepalive_timeout       75s;
    send_timeout            60s;

    upstream app_backend {
        server localhost:3000;
        keepalive 32;
        keepalive_timeout 60s;
        keepalive_requests 1000;
    }

    server {
        listen 443 ssl;

        # ─── API biasa ────────────────────────────────────────────────────────
        location /api/ {
            proxy_pass http://app_backend;
            proxy_http_version 1.1;
            proxy_set_header Connection "";
            proxy_read_timeout 30s;  # Override default untuk API
        }

        # ─── Operasi berat ────────────────────────────────────────────────────
        location /api/export/ {
            proxy_pass http://app_backend;
            proxy_http_version 1.1;
            proxy_set_header Connection "";
            proxy_read_timeout     300s;
            proxy_buffer_size       64k;
            proxy_buffers           8 64k;
        }

        # ─── SSE / Streaming ──────────────────────────────────────────────────
        location /events/ {
            proxy_pass http://app_backend;
            proxy_http_version 1.1;
            proxy_set_header Connection '';
            proxy_buffering off;
            proxy_read_timeout 3600s;
        }

        # ─── Upload ───────────────────────────────────────────────────────────
        location /upload/ {
            proxy_pass http://app_backend;
            client_max_body_size    100m;
            client_body_buffer_size  10m;
            proxy_send_timeout      300s;
            proxy_read_timeout       60s;
        }
    }
}

Tuning Buffer untuk Kasus Khusus #

Aplikasi dengan Response Header Besar #

Beberapa framework mengirim banyak header (cookie, custom headers, debug info) yang bisa melebihi proxy_buffer_size default (4k):

# Gejala: error "upstream sent too big header"
# Solusi: naikkan proxy_buffer_size

location / {
    proxy_pass http://backend;

    # Naikkan buffer untuk header (default: 4k, biasanya cukup)
    # Naikkan jika melihat error "upstream sent too big header"
    proxy_buffer_size 16k;

    # Sesuaikan juga proxy_buffers jika response body besar
    proxy_buffers 8 16k;    # 8 buffer @ 16k = 128k total
    proxy_busy_buffers_size 32k;
}
# Diagnosa: cek apakah error terkait buffer muncul di log
grep -i "upstream sent too big header" /var/log/nginx/error.log

# Ukur ukuran header response backend
curl -v http://localhost:3000/api/test 2>&1 | grep -E "^<"
# Hitung total ukuran header

Micro-Buffering untuk Latency Rendah #

Untuk API yang sangat latency-sensitive (fintech, gaming), kita bisa mengoptimalkan agar data segera diteruskan ke klien:

location /api/realtime/ {
    proxy_pass http://realtime_backend;

    # Buffer minimal — segera teruskan ke klien
    proxy_buffer_size 4k;
    proxy_buffers 2 4k;      # Total hanya 8k
    proxy_busy_buffers_size 4k;

    # Nonaktifkan file temp (tidak boleh ada delay untuk tulis disk)
    proxy_max_temp_file_size 0;

    # TCP_NODELAY: kirim paket segera tanpa menunggu Nagle algorithm
    # (biasanya sudah on secara default)
    tcp_nodelay on;
}

Monitoring Buffer Usage #

# Lihat apakah Nginx sedang menggunakan file temp (tanda buffer penuh)
ls -la /var/cache/nginx/temp/
# Jika banyak file di sini, proxy_buffers perlu dinaikkan
# atau ada klien yang sangat lambat mengunduh

# Cek koneksi yang sedang aktif
nginx -s status
# Atau jika ada stub_status:
curl http://localhost/nginx_status

# Monitor penggunaan memori Nginx (terkait buffer)
cat /proc/$(pgrep -o nginx)/status | grep -E "VmRSS|VmPeak"

Ringkasan #

  • Buffering on (default): backend bebas cepat, Nginx mengurus pengiriman ke klien yang lambat — baik untuk semua web app biasa. Nonaktifkan dengan proxy_buffering off untuk SSE, streaming, atau WebSocket.
  • proxy_connect_timeout: timeout membuka koneksi — terpicu saat backend tidak berjalan. proxy_read_timeout: timeout idle antar paket dari backend, bukan total waktu — naikkan untuk operasi berat.
  • proxy_read_timeout 300s untuk endpoint yang memproses laporan/export; timeout default 60s terlalu pendek untuk ini.
  • Keepalive di upstream block (keepalive 32) + proxy_http_version 1.1 + proxy_set_header Connection "" menghemat ribuan TCP handshake per detik.
  • 502 = backend tidak bisa dihubungi; 504 = backend terlalu lambat; 499 = klien disconnected duluan — masing-masing butuh penanganan berbeda.
  • Gunakan $upstream_response_time di format log untuk mengukur berapa lama backend merespons secara aktual.

← Sebelumnya: Proxy Headers   Berikutnya: Proxy Cache →

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