WebSocket

WebSocket #

WebSocket memungkinkan komunikasi dua arah antara browser dan server melalui satu koneksi TCP yang persisten — berbeda dari HTTP biasa yang request-response dan stateless. Nginx mendukung proxying WebSocket, tapi butuh konfigurasi tambahan karena WebSocket dimulai sebagai HTTP upgrade request.

Cara Kerja WebSocket Upgrade #

1. Browser kirim HTTP request dengan header Upgrade:
   GET /ws HTTP/1.1
   Host: example.com
   Upgrade: websocket
   Connection: Upgrade
   Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
   Sec-WebSocket-Version: 13

2. Server merespons 101 Switching Protocols:
   HTTP/1.1 101 Switching Protocols
   Upgrade: websocket
   Connection: Upgrade

3. Koneksi TCP tetap terbuka
   Browser ↔ Server: komunikasi dua arah secara real-time

Nginx perlu meneruskan header Upgrade dan Connection ke backend agar proses upgrade ini bisa terjadi.


Konfigurasi Dasar WebSocket #

server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    location /ws/ {
        proxy_pass http://websocket_backend;

        # Dua baris wajib untuk WebSocket
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Header standar
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # Timeout untuk koneksi WebSocket yang long-lived
        # Default proxy_read_timeout hanya 60s — terlalu pendek untuk WebSocket
        proxy_read_timeout 86400s;     # 24 jam
        proxy_send_timeout 86400s;
        proxy_connect_timeout 5s;
    }
}

Nilai $http_upgrade diteruskan apa adanya dari request klien — bisa kosong untuk request HTTP biasa, atau “websocket” untuk upgrade request. Connection "upgrade" memberitahu backend bahwa ini adalah upgrade request.


Kombinasi HTTP dan WebSocket di Satu Server #

Aplikasi real-time biasanya punya endpoint WebSocket dan endpoint HTTP REST di server yang sama. Map variable membantu routing:

http {
    # Deteksi apakah ini WebSocket upgrade request
    map $http_upgrade $connection_upgrade {
        default     upgrade;    # Untuk WebSocket
        ''          close;      # Untuk HTTP biasa: tutup koneksi setelah response
    }

    upstream app_backend {
        server localhost:3000;
        keepalive 32;
    }

    server {
        listen 443 ssl;
        server_name example.com;

        ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

        # Semua request (HTTP dan WebSocket) ke backend yang sama
        location / {
            proxy_pass http://app_backend;
            proxy_http_version 1.1;

            # Gunakan variabel dari map
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;

            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;

            # Timeout panjang untuk WebSocket
            proxy_read_timeout 86400s;
        }

        # Endpoint HTTP khusus dengan timeout lebih pendek (optional)
        location /api/ {
            proxy_pass http://app_backend;
            proxy_http_version 1.1;
            proxy_set_header Connection "";
            proxy_read_timeout 30s;   # Timeout lebih pendek untuk API
        }
    }
}

Pattern dengan map $http_upgrade $connection_upgrade adalah idiom standar untuk menangani keduanya secara elegan dalam satu konfigurasi.


Load Balancing WebSocket #

WebSocket adalah koneksi stateful — sekali klien terhubung ke server tertentu, semua pesan dalam sesi itu harus ke server yang sama. Gunakan ip_hash atau hash untuk session affinity:

upstream ws_servers {
    ip_hash;   # Pastikan klien yang sama selalu ke server yang sama

    server 10.0.0.1:3000;
    server 10.0.0.2:3000;
    server 10.0.0.3:3000;
}

server {
    location /ws/ {
        proxy_pass http://ws_servers;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 86400s;
    }
}

Atau gunakan hash berdasarkan cookie session untuk konsistensi yang lebih baik dari ip_hash:

upstream ws_servers {
    hash $cookie_session_id consistent;
    server 10.0.0.1:3000;
    server 10.0.0.2:3000;
}

Monitoring Koneksi WebSocket #

# Lihat jumlah koneksi WebSocket yang aktif
# State ESTABLISHED ke port backend = koneksi WebSocket aktif
ss -tn | grep :3000 | grep ESTABLISHED | wc -l

# Monitoring lebih detail
ss -tnp | grep nginx | awk '{print $1}' | sort | uniq -c

# Di log Nginx, koneksi WebSocket terlihat sebagai request dengan
# status 101 dan bytes_sent yang terus bertambah
grep ' 101 ' /var/log/nginx/access.log | tail -20

Troubleshooting WebSocket #

Koneksi WebSocket langsung terputus setelah 60 detik: Penyebab: proxy_read_timeout default 60 detik terlampaui. Solusi: Naikkan proxy_read_timeout atau implementasikan heartbeat/ping-pong di aplikasi.

Error 400 Bad Request saat upgrade: Penyebab: Header Upgrade atau Connection tidak diteruskan dengan benar. Solusi: Pastikan proxy_http_version 1.1 dan proxy_set_header Upgrade $http_upgrade ada di konfigurasi.

WebSocket bekerja dengan HTTP tapi tidak dengan HTTPS: Penyebab: JavaScript menggunakan ws:// (bukan wss://) untuk koneksi WebSocket di halaman HTTPS. Solusi: Pastikan kode frontend menggunakan wss:// untuk koneksi WebSocket di halaman HTTPS.


Ringkasan #

  • Dua directive wajib untuk WebSocket: proxy_http_version 1.1 dan proxy_set_header Upgrade $http_upgrade + proxy_set_header Connection "upgrade".
  • Naikkan proxy_read_timeout ke nilai yang besar (misalnya 86400s) — default 60 detik akan memutus koneksi WebSocket yang lama idle.
  • Gunakan map $http_upgrade $connection_upgrade untuk menangani HTTP dan WebSocket di satu location secara elegan.
  • WebSocket bersifat stateful — gunakan ip_hash atau hash $cookie_... untuk session affinity saat load balancing.
  • Di halaman HTTPS, koneksi WebSocket harus menggunakan wss://, bukan ws://.

← Sebelumnya: Python WSGI   Berikutnya: SPA React/Vue →

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