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:2pxDirective 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? → 504proxy_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 Error | Penyebab | Cara Debug |
|---|---|---|
| 502 Bad Gateway | Backend tidak bisa dihubungi, koneksi ditolak, atau proxy_connect_timeout terlampaui | Cek apakah backend berjalan: curl localhost:3000 |
| 504 Gateway Timeout | Backend terlalu lama merespons — proxy_read_timeout terlampaui | Naikkan proxy_read_timeout atau optimalkan backend |
| 413 Request Entity Too Large | Body request melebihi client_max_body_size | Naikkan client_max_body_size untuk endpoint upload |
| 499 Client Closed Request | Klien memutus koneksi sebelum backend selesai | Klien 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 offuntuk 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 300suntuk endpoint yang memproses laporan/export; timeout default 60s terlalu pendek untuk ini.- Keepalive di
upstreamblock (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_timedi format log untuk mengukur berapa lama backend merespons secara aktual.