Keepalive #

Untuk setiap request HTTP yang dikirimkan oleh browser klien, sistem operasi di latar belakang harus terlebih dahulu membuka koneksi jaringan di tingkat protokol TCP. Membuka koneksi baru bukanlah proses yang instan; ia membutuhkan proses jabat tangan (handshake) bolak-balik antara klien dan server. Ketika sebuah halaman web modern memuat puluhan aset statis secara bersamaan, membuka koneksi TCP baru untuk setiap aset adalah tindakan yang sangat tidak efisien.

Di sinilah fitur Keepalive memainkan peran krusial. Keepalive memungkinkan satu koneksi TCP yang sama dipertahankan tetap terbuka setelah sebuah transaksi request selesai, sehingga request berikutnya dapat langsung dikirimkan melalui koneksi tersebut tanpa perlu melakukan jabat tangan ulang. Di artikel ini, kita akan membahas mengapa proses pembuatan koneksi TCP sangat mahal, cara menyetel koneksi keepalive di sisi klien, mengonfigurasi connection pooling ke backend upstream secara benar, mengaktifkan keepalive untuk PHP-FPM, serta menggunakan utilitas CLI Linux untuk memverifikasi keefektifan koneksi keepalive kita.

Mengapa Membuka Koneksi Baru Sangat Mahal? #

Ada tiga faktor utama yang membuat pembuatan koneksi TCP baru secara berulang-ulang menjadi musuh utama bagi performa server dengan lalu lintas tinggi:

  1. TCP Three-Way Handshake: Proses pembukaan koneksi TCP membutuhkan tiga langkah pengiriman paket: klien mengirim paket SYN, server menjawab dengan SYN-ACK, dan klien mengirim kembali paket ACK. Ini membutuhkan satu Round-Trip Time (RTT) penuh sebelum data HTTP pertama dapat dikirimkan.
  2. TLS Handshake Overhead: Di era HTTPS modern, setelah koneksi TCP terbentuk, server dan browser harus melakukan jabat tangan enkripsi TLS. Proses ini membutuhkan pertukaran sertifikat keamanan dan negosiasi kunci enkripsi yang memakan waktu 1 hingga 2 RTT tambahan serta mengonsumsi daya komputasi CPU yang tinggi untuk proses kriptografi.
  3. Masalah Sockets TIME_WAIT: Ketika koneksi TCP ditutup, soket tidak langsung terhapus dari sistem operasi. Soket akan masuk ke dalam kondisi TIME_WAIT selama 60 hingga 120 detik (tergantung konfigurasi kernel Linux) untuk memastikan tidak ada paket data liar yang tertinggal di jaringan. Jika server kita memproses ribuan request baru per detik tanpa keepalive, server akan kehabisan alokasi nomor port lokal (local port exhaustion) karena puluhan ribu soket tertahan di status TIME_WAIT.

Dengan menggunakan keepalive, kita dapat memangkas latensi RTT dan negosiasi TLS ini hingga 90% untuk request-request berikutnya.


Keepalive Koneksi Sisi Klien (Browser ke Nginx) #

Keepalive ke klien mengontrol bagaimana Nginx mengelola koneksi TCP terbuka dengan browser pengguna setelah respons selesai dikirimkan. Kita mengaturnya menggunakan beberapa direktif di dalam blok http atau server:

http {
    # 1. Batas waktu koneksi idle dipertahankan (default Nginx adalah 75s)
    # Sweet spot produksi yang disarankan adalah 60 hingga 65 detik
    keepalive_timeout 65s;

    # 2. Jumlah request maksimum yang boleh dikirimkan melalui satu koneksi keepalive
    # Setelah mencapai batas ini, Nginx akan menutup koneksi secara paksa
    keepalive_requests 1000;

    # 3. Waktu maksimum satu koneksi keepalive boleh dipertahankan secara total
    keepalive_time 1h;
}

Memahami Parameter Keepalive Klien #

  • keepalive_timeout 65s;: Nginx akan mempertahankan koneksi TCP tetap terbuka selama 65 detik setelah aktivitas request terakhir selesai. Jika dalam 65 detik pengguna tidak melakukan aktivitas apa pun (tidak mengklik link atau memuat data baru), Nginx akan menutup koneksi secara aman. Menyetel nilai ini terlalu lama (misal 5 menit) dapat menghabiskan kuota worker_connections kita oleh koneksi-koneksi kosong pengunjung yang sudah tidak aktif.
  • keepalive_requests 1000;: Browser modern sangat agresif dalam memuat aset web. Nilai default Nginx yang lama (100) sering kali terlalu kecil untuk aplikasi web berbasis Single Page Application (SPA). Menyetel batas ke 1000 atau lebih memastikan browser tidak perlu melakukan jabat tangan TCP berulang kali di tengah-tengah pemuatan halaman web.

Keepalive Koneksi Sisi Upstream (Nginx ke Backend) #

Tuning performa yang paling sering dilewatkan oleh administrator sistem adalah mengonfigurasi koneksi keepalive antara Nginx dengan server backend (seperti aplikasi Node.js, Go, Python, dll.).

Secara default, Nginx bertindak sebagai reverse proxy yang bersifat stateless: untuk setiap request yang diteruskan ke backend, Nginx akan membuka koneksi TCP baru, mengambil respons dari backend, lalu langsung menutup koneksi tersebut. Perilaku default ini membuang resource server secara luar biasa pada lalu lintas tinggi.

Kita harus mengaktifkan Connection Pooling di dalam blok upstream:

upstream app_backend {
    server 10.0.0.10:3000;
    server 10.0.0.11:3000;

    # Pertahankan maksimal 32 koneksi idle di memori bersama untuk setiap server
    keepalive 32;

    # Waktu maksimum koneksi idle di pool dipertahankan
    keepalive_timeout 60s;

    # Jumlah request maksimal per koneksi pool sebelum diganti baru
    keepalive_requests 2000;
}

Kewajiban Konfigurasi di Server Block #

Mendeklarasikan keepalive di blok upstream saja tidak cukup. Kita wajib menyisipkan dua direktif tambahan di dalam blok location agar Nginx menggunakan protokol HTTP/1.1 yang mendukung keepalive secara bawaan ke backend:

server {
    listen 80;
    server_name app.unisbadri.com;

    location / {
        proxy_pass http://app_backend;

        # 1. WAJIB: Gunakan HTTP/1.1 (default proxy_pass menggunakan HTTP/1.0 yang tidak mendukung keepalive)
        proxy_http_version 1.1;

        # 2. WAJIB: Kosongkan header Connection (menghapus instruksi "close" dari request klien)
        proxy_set_header Connection "";

        proxy_set_header Host $host;
    }
}

[!IMPORTANT] Tanpa mendeklarasikan proxy_http_version 1.1; dan proxy_set_header Connection "";, Nginx akan tetap menggunakan protokol HTTP/1.0 dan mengirimkan header Connection: close ke backend untuk setiap request, membuat parameter keepalive di blok upstream kita menjadi sia-sia.

Berikut adalah ilustrasi perbedaan alur koneksi tanpa keepalive vs dengan keepalive:

flowchart TD
    subgraph "Tanpa Keepalive (HTTP/1.0)"
        c1["Browser Klien"] -->|"1. TCP SYN / SSL"| n1["Nginx Proxy"]
        n1 -->|"2. TCP SYN (Koneksi Baru)"| b1["Backend API"]
        b1 -->|"3. Kirim Respons"| n1
        n1 -->|"4. Tutup Koneksi TCP"| b1
        n1 -->|"5. Kirim Respons & Tutup TCP"| c1
    end

    subgraph "Dengan Keepalive (HTTP/1.1 Connection Pool)"
        c2["Browser Klien"] -->|"1. Satu TCP Handshake / SSL"| n2["Nginx Proxy"]
        n2 -->|"2. Gunakan Koneksi Idle di Pool"| b2["Backend API"]
        b2 -->|"3. Kirim Respons"| n2

        c2 -->|"4. Kirim Request Berikutnya"| n2
        n2 -->|"5. Reuse Koneksi di Pool"| b2
    end

    classDef default fill:#f9f9f9,stroke:#d1d5db,stroke-width:1px,color:#111827;
    classDef poolStyle fill:#d1fae5,stroke:#10b981,stroke-width:2px,color:#065f46;
    class n2,b2 poolStyle;

Menentukan Kapasitas Pool Keepalive yang Tepat #

Nilai parameter keepalive di blok upstream menentukan jumlah koneksi idle maksimum yang akan dipertahankan di memori bersama (shared memory pool) untuk setiap backend server. Nilai ini bukan batasan jumlah total koneksi aktif, melainkan hanya cadangan koneksi siap pakai saat kondisi server sedang senggang.

Kita dapat menentukan ukuran pool keepalive menggunakan panduan beban kerja berikut:

  • Trafik Rendah (< 100 req/s): keepalive 8;
  • Trafik Sedang (100 - 1.000 req/s): keepalive 32;
  • Trafik Tinggi (> 1.000 req/s): keepalive 64; hingga keepalive 128;

Risiko Nilai Keepalive Terlalu Tinggi #

Jangan menyetel nilai keepalive terlalu tinggi secara berlebihan (misalnya menyetel keepalive 1024 untuk server trafik rendah). Tindakan ini akan memaksa server backend kita membiarkan ribuan koneksi terbuka secara idle. Padahal, aplikasi backend (seperti Node.js atau server database PostgreSQL) memiliki batasan jumlah koneksi maksimum yang jauh lebih ketat daripada Nginx. Hal ini dapat memicu error kehabisan soket di sisi backend (backend connection limits reached).


Keepalive untuk FastCGI / PHP-FPM #

Jika backend kita adalah PHP-FPM yang terhubung ke Nginx menggunakan protokol FastCGI, kita mengonfigurasi keepalive secara berbeda karena FastCGI memiliki spesifikasi protokol koneksi tersendiri.

Kita harus mengaktifkan direktif fastcgi_keep_conn on; di dalam blok server location kita:

upstream php_backend {
    # Gunakan soket Unix untuk performa lokal optimal
    server unix:/run/php/php8.2-fpm.sock;

    # Buat pool keepalive untuk PHP-FPM
    keepalive 16;
}

server {
    listen 80;
    server_name myphpapp.com;

    location ~ \.php$ {
        fastcgi_pass php_backend;

        # WAJIB: Aktifkan keepalive khusus untuk protokol FastCGI
        fastcgi_keep_conn on;

        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

Skenario Unix Socket vs TCP Loopback pada PHP-FPM #

Pada arsitektur lokal di mana Nginx dan PHP-FPM berjalan di satu mesin server yang sama, penggunaan Unix Domain Socket (server unix:/run/php/php8.2-fpm.sock;) adalah pilihan yang sangat direkomendasikan karena menghindari overhead stack jaringan TCP/IP lokal (TCP loopback). Namun, Unix socket tidak dapat diskalakan jika kita ingin menaruh server PHP-FPM di server terpisah (dedicated application servers).

Jika kita harus memindahkan backend PHP-FPM ke host eksternal melalui jaringan, kita wajib beralih menggunakan TCP Socket (misalnya server 10.0.0.25:9000;). Dalam skenario TCP multi-server inilah mengaktifkan fastcgi_keep_conn on; bersama pooling keepalive menjadi sangat penting. Tanpa keepalive, Nginx akan memicu pembuatan jutaan koneksi TCP baru lintas server yang dapat mengakibatkan kemacetan antrean jaringan fisik (network congestion) dan kehabisan alokasi port lokal.


Memverifikasi Keepalive Berjalan di Produksi #

Kita dapat membuktikan apakah koneksi keepalive Nginx ke backend telah berjalan secara optimal dengan memantau aktivitas soket di server menggunakan utilitas terminal Linux bawaan seperti ss (socket statistics).

Jalankan perintah berikut pada terminal server produksi saat melayani trafik:

# Saring koneksi TCP aktif ke port backend kita (misal port 3000)
ss -tn state established | grep :3000

Menganalisis Pola Koneksi #

  • Jika Keepalive TIDAK Berfungsi: Kita akan melihat daftar koneksi yang berubah-ubah dengan cepat. Jumlah koneksi dengan status TIME_WAIT akan sangat menumpuk di sisi Nginx:
    # Periksa jumlah status koneksi socket ke backend
    ss -atn | grep :3000 | awk '{print $1}' | sort | uniq -c
    
    Output yang salah akan menampilkan ribuan baris TIME-WAIT dan sangat sedikit ESTAB.
  • Jika Keepalive BERFUNGSI: Kita akan melihat kumpulan koneksi dengan status ESTAB (Established) bernilai stabil dan konstan. Jumlah status TIME-WAIT ke arah port backend akan sangat minim karena Nginx menggunakan kembali koneksi yang sudah terbuka secara konsisten.

Tuning TCP Keepalive di Tingkat Kernel Linux #

Selain mengonfigurasi parameter keepalive di dalam berkas konfigurasi Nginx, performa dan stabilitas soket jaringan juga sangat bergantung pada bagaimana kernel sistem operasi Linux mengelola TCP keepalive secara umum. Secara default, pengaturan kernel Linux untuk mendeteksi apakah suatu koneksi idle masih hidup atau sudah mati (dead TCP connections) dirancang untuk jaringan komputer lama yang lambat.

Sebagai contoh, default kernel Linux mempertahankan koneksi TCP mati selama 7200 detik (2 jam) sebelum mengirimkan probe penyelidikan. Di server web produksi, membiarkan soket mati menumpuk selama 2 jam adalah pemborosan memori kernel yang berbahaya.

Kita dapat mengoptimalkan parameter TCP keepalive kernel dengan menambahkan baris berikut pada berkas /etc/sysctl.conf di server host kita:

# /etc/sysctl.conf
# Optimasi TCP Keepalive Kernel

# Waktu tunggu (detik) sebelum mengirim probe pertama kali pada koneksi idle
net.ipv4.tcp_keepalive_time = 300

# Jeda waktu (detik) antar pengiriman probe penyelidikan berikutnya
net.ipv4.tcp_keepalive_intvl = 15

# Jumlah kegagalan respons probe sebelum koneksi diputuskan secara paksa
net.ipv4.tcp_keepalive_probes = 5

Setelah file disunting, jalankan perintah berikut untuk menerapkan perubahan ke kernel tanpa perlu merestart server:

sudo sysctl -p

Dengan konfigurasi ini, jika ada koneksi TCP keepalive yang terputus sepihak (misalnya perangkat handphone klien kehilangan sinyal internet secara mendadak), kernel Linux kita akan mendeteksinya dalam waktu 5 menit (bukan 2 jam) dan langsung membebaskan nomor port serta resource memori soket tersebut secara otomatis.


Evolusi Protokol: HTTP/1.1 Keepalive vs HTTP/2 & HTTP/3 Multiplexing #

Penting bagi kita untuk memahami bagaimana peran koneksi Keepalive berubah seiring dengan adopsi protokol web baru:

  • HTTP/1.1 (Keepalive): Request dikirimkan secara berurutan (sequential). Jika ada 10 gambar yang harus dimuat, browser harus menunggu gambar 1 selesai diunduh sebelum dapat mengirimkan request untuk gambar 2 di atas koneksi TCP keepalive yang sama. Ini memicu masalah Head-of-Line Blocking (HoLB) di tingkat HTTP. Untuk mempercepat, browser biasanya membuka hingga 6 koneksi TCP keepalive paralel sekaligus ke satu domain server.
  • HTTP/2 (Multiplexing): Memperkenalkan fitur Multiplexing. Browser hanya membuka satu koneksi TCP tunggal ke server Nginx kita. Melalui satu koneksi tersebut, browser dapat mengirimkan puluhan request dan menerima puluhan respons secara bersamaan (concurrently) tanpa saling mengantre. Ini menghilangkan kebutuhan membuka banyak koneksi paralel dan secara radikal mengurangi beban kerja handshake di Nginx.
  • HTTP/3 (QUIC over UDP): Melangkah lebih jauh dengan mengganti protokol transport TCP menjadi UDP menggunakan protokol QUIC. Pada HTTP/3, tidak ada lagi proses jabat tangan TCP standar. Sebagai gantinya, QUIC mengimplementasikan sistem jabat tangan satu langkah terenkripsi secara bawaan dan menggunakan Connection ID yang unik, memungkinkan pengguna berpindah jaringan (misal dari Wi-Fi kantor ke koneksi 4G seluler) tanpa perlu melakukan pemutusan dan penyambungan ulang koneksi jaringan dari awal.

Meskipun HTTP/2 dan HTTP/3 mengurangi kompleksitas keepalive di sisi klien browser, konfigurasi keepalive connection pool di sisi backend (Nginx ke upstream backend) tetap 100% wajib kita terapkan karena komunikasi proxy internal tersebut sebagian besar masih mengandalkan protokol HTTP/1.1 TCP standar.


Ringkasan dan Praktik Terbaik #

  • Setel keepalive_timeout ke 65s: Pertahankan koneksi idle browser klien dalam batas wajar guna menyeimbangkan memori RAM server dengan performa browser.
  • Wajib Gunakan HTTP/1.1 ke Backend: Selalu deklarasikan proxy_http_version 1.1; dan proxy_set_header Connection ""; saat mengaktifkan keepalive di blok upstream.
  • Gunakan fastcgi_keep_conn: Aktifkan fitur ini khusus untuk backend PHP-FPM agar koneksi soket FastCGI di-reuse secara berkelanjutan.
  • Sesuaikan Ukuran Pool: Atur nilai keepalive di blok upstream secara proporsional sesuai dengan tingkat request per detik untuk menghindari kehabisan soket di sisi backend.
  • Pantau Sockets TIME_WAIT: Gunakan perintah ss secara berkala untuk memverifikasi kesehatan koneksi jaringan internal antara proxy Nginx dengan backend kita.

← Sebelumnya: Caching   Berikutnya: Open File Cache →

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