WebSocket Nginx Yapılandırması: Production'da Kopma Sorunlarını Önleme
WebSocket bağlantılarınız nginx arkasında kopuyorsa sorun büyük olasılıkla config'de. Upgrade başlığı, timeout, WSS ve load balancing için net teknik rehber.
WebSocket sunucusu nginx arkasında çalıştırıldığında sıradan HTTP reverse proxy yapılandırması yetmez. HTTP Upgrade mekanizması, uzun süreli açık bağlantılar ve çok sunuculu senaryolar nginx'te standart dışı ayarlar gerektirir. Pek çok kurulum temel handshake'i geçer ama dakikalar içinde kopan bağlantılar, yük dengeleme sorunları veya WSS hataları yaşar. Bu rehberde temel proxy config'inden SSL sonlandırmaya, timeout yönetiminden load balancing'e kadar production için kritik her ayarı somut örneklerle ele alacağız.
WebSocket Nginx'te Neden Özel Yapılandırma İster?
HTTP/1.1 tabanlı WebSocket protokolü bağlantıyı standart bir HTTP isteğiyle başlatır; ancak bu istek Connection: Upgrade ve Upgrade: websocket başlıklarını taşır. Nginx bu başlıkları varsayılan olarak downstream proxy'ye iletmez — Connection başlığını siler, Upgrade başlığını boşaltır. Sonuç: WebSocket handshake başarısız olur, istemci 101 Switching Protocols yerine 400 ya da 502 alır.
Buna ek olarak iki önemli fark daha vardır:
- Uzun süreli bağlantı: HTTP isteği saniyeler içinde tamamlanır; WebSocket bağlantısı saatlerce açık kalabilir. Nginx'in varsayılan
proxy_read_timeoutdeğeri 60 saniyedir — aktivite olmayan WebSocket bağlantısını bu süre dolunca keser. - Durum bilgisi (stateful): Birden fazla backend sunucu varsa, aynı istemcinin her isteğinin aynı sunucuya ulaşması gerekir. Nginx'in varsayılan round-robin algoritması bunu garanti etmez.
Temel WebSocket Proxy Yapılandırması
Çalışan minimum bir WebSocket nginx config'i şu üç ek ayar olmadan tamamlanmaz:
http {
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name ws.example.com;
location /ws/ {
proxy_pass http://websocket_backend;
proxy_http_version 1.1;
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;
}
}
upstream websocket_backend {
server 127.0.0.1:3000;
}
}
proxy_http_version 1.1 zorunludur. HTTP/1.0, Connection: Upgrade başlığını desteklemez; bu satır olmadan handshake hiç başlamaz.
map bloğu iki işlev üstlenir: WebSocket isteği geldiğinde ($http_upgrade dolu) Connection değerini upgrade olarak ayarlar; istek normal HTTP ise ($http_upgrade boş) değeri close yapar. Aynı location'dan hem WebSocket hem HTTP trafiği geçiyorsa bu ayrım gereklidir.
Upgrade ve Connection başlıklarının proxy_set_header ile açıkça iletilmesi handshake'in nginx'ten backend'e eksiksiz ulaşmasını sağlar.
Bağlantı Kopmasını Önleyen Timeout Ayarları
Production ortamında en sık şikayet kaynağı budur: bağlantı kurulur, bir süre çalışır, sonra sessizce düşer. Nedeni çoğunlukla nginx timeout değerleridir.
| Direktif | Varsayılan | WebSocket İçin Önerilen |
|---|---|---|
proxy_read_timeout |
60s | 3600s |
proxy_send_timeout |
60s | 3600s |
proxy_connect_timeout |
60s | 10s (değiştirme) |
location /ws/ {
proxy_pass http://websocket_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
proxy_connect_timeout backend'e ilk TCP bağlantısı içindir; artırmanın anlamı yoktur — backend yanıt vermiyorsa uzun beklemek sorunu çözmez, sadece hata gecikmesini uzatır.
Timeout yeterli değildir. Nginx ile backend arasındaki ara güvenlik duvarları veya NAT cihazları kendi idle timeout politikalarını uygular ve WebSocket bağlantısını nginx'e haber vermeden keser. Buna karşı önlem uygulama katmanında ping/pong kullanmaktır: her 30–45 saniyede bir ping çerçevesi göndermek bağlantıyı aktif tutar.
SSL/WSS Yapılandırması
HTTPS kullanan bir sitede WebSocket ws:// yerine wss:// protokolüyle kurulur. SSL sonlandırma nginx'te yapılıyorsa backend'e düz ws:// ile iletim yeterlidir; nginx TLS'yi uygulama sunucusu adına yönetir.
server {
listen 443 ssl;
server_name ws.example.com;
ssl_certificate /etc/letsencrypt/live/ws.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ws.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location /ws/ {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}
server {
listen 80;
server_name ws.example.com;
return 301 https://$host$request_uri;
}
X-Forwarded-Proto başlığını eklemek zorunludur. Uygulama bu başlığı okuyarak bağlantının HTTPS üzerinden geldiğini anlar ve kendi ürettiği URL'leri ws:// yerine wss:// olarak oluşturur. Bu başlık eksik olduğunda bazı framework'ler mixed content hatası üretir.
Load Balancing: ip_hash Neden Zorunludur?
Birden fazla backend sunucu çalıştırıyorsanız nginx'in varsayılan round-robin algoritması WebSocket'te oturum (session) tutarsızlığına yol açar.
Senaryo: İstemci WebSocket bağlantısını backend A'ya kurar, uygulama bu bağlantıya ait state'i A'da depolar. Bağlantı kopup yeniden kurulduğunda nginx isteği backend B'ye gönderir; B'nin bu istemciyle ilgili hiçbir bilgisi yoktur. Kimlik doğrulama başarısız olur ya da oturum sıfırlanır.
Çözüm ip_hash ile aynı istemci IP'sini her zaman aynı backend'e yönlendirmektir:
upstream websocket_backend {
ip_hash;
server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;
server 127.0.0.1:3001 max_fails=3 fail_timeout=30s;
server 127.0.0.1:3002 max_fails=3 fail_timeout=30s;
}
ip_hash sınırlaması: Çok sayıda kullanıcı aynı NAT arkasından (kurumsal ağ, mobil operatör) bağlanıyorsa tüm bu kullanıcılar tek bir backend'e yığılır ve yük dengesi bozulur. Bu durumda iki alternatif vardır:
- Redis Pub/Sub ile state paylaşımı: Tüm backend'ler ortak bir Redis'e yazarsa hangi backend'e düşülürse düşülsün state korunur;
ip_hashzorunluluğu ortadan kalkar. - Cookie tabanlı yönlendirme: nginx Plus veya üçüncü taraf
stickymodülü kullanılarak ilk WebSocket bağlantısında istemciye backend kimliği içeren bir cookie atanır.
Buffer Ayarları ve Boyut Optimizasyonu
WebSocket mesajları küçük (ping/pong, JSON event) veya büyük (dosya transfer, video chunk) olabilir. Nginx'in varsayılan buffer boyutları küçük HTTP yanıtları için ayarlanmıştır; büyük WebSocket mesajlarında disk geçici dosya yazımı (proxy_temp_path) devreye girer ve gecikme artar.
location /ws/ {
proxy_pass http://websocket_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
proxy_buffer_size 4k;
}
proxy_buffering off WebSocket için en temiz seçenektir: nginx mesajları buffer'lamadan doğrudan istemciye iletir, gecikme düşer. Bu ayar aktifken proxy_buffer_size sadece response başlığı için geçerli olur; 4k standart başlıklar için yeterlidir.
Yalnızca büyük binary yük aktarımı yapan bir WebSocket uygulamasında (örneğin canlı video) proxy_buffering on ile birlikte proxy_buffers 16 64k; proxy_buffer_size 64k; kombinasyonu throughput'u artırabilir. İki seçeneği kendi yük tipinize göre kıyaslayın.
Sorun Giderme: Config Test ve Log Analizi
Yapılandırma değişikliklerini production'a almadan önce sözdizimini doğrulayın:
nginx -t
nginx: configuration file /etc/nginx/nginx.conf test is successful çıktısını görmeden systemctl reload nginx çalıştırmayın; hatalı config nginx'i başlatamaz duruma getirebilir.
WebSocket bağlantı sorunlarını hızlı ayıklamak için access log formatını genişletin:
log_format ws_debug '$remote_addr [$time_local] "$request" '
'status=$status upgrade="$http_upgrade" '
'upstream=$upstream_addr';
access_log /var/log/nginx/ws.log ws_debug;
Durum kodlarının anlamı:
| HTTP Durum Kodu | Anlam |
|---|---|
| 101 | Handshake başarılı, WebSocket aktif |
| 400 | Upgrade başlığı eksik veya hatalı — config sorunu |
| 502 | Backend ulaşılamaz veya port kapalı |
| 504 | proxy_read_timeout doldu — timeout değeri artırılmalı |
Gerçek zamanlı izleme için 101 dışındaki kodları filtreleyin:
tail -f /var/log/nginx/ws.log | grep -v ' status=101 '
Eğer $http_upgrade alanı log'da boş geliyorsa istemci tarafı WebSocket isteği doğru göndermiyor demektir; sunucu config'inden önce istemci kodunu kontrol edin.
Sonuç
WebSocket'i nginx arkasında kararlı biçimde çalıştırmak için dört ayar kritiktir: Upgrade/Connection başlıklarının iletilmesi, proxy_read_timeout değerinin artırılması, wss:// için doğru SSL yapılandırması ve çok sunuculu ortamda ip_hash kullanımı. Bu dört nokta eksik veya yanlış olduğunda bağlantılar ya hiç kurulamaz ya da düzensiz aralıklarla kopar.
Yapılandırmanızı hazırladıktan sonra nginx -t ile sözdizimini doğrulayın, systemctl reload nginx ile uygulayın ve genişletilmiş log formatıyla ilk bağlantıları izleyin. Production'a geçmeden önce yüksek gecikme, VPN ve mobil ağ koşullarında bağlantı kararlılığını test etmek, sonradan yaşanacak kopma şikayetlerinin büyük bölümünü önceden önler.
Hosting karşılaştırması yapmaya hazır mısın?
100+ firmanın fiyatlarını tek tıkla karşılaştır, en uygun paketi bul.
Hosting Karşılaştır →İlgili Yazılar
Sunucuda Port Taramalarını Tespit Etme: Net İzleme ve Log Rehberi
Port taraması tespiti için doğru loglar, fail2ban/iptables/WAF kontrolleri, anomali eşikleri ve olay akışı adımlarını net bir rehberle öğrenin.
DKIM, SPF, DMARC: E-posta Deliverability Net Rehberi
DKIM, SPF ve DMARC ayarlarını doğru kurun: kayıt örnekleri, test adımları, yaygın hatalar ve deliverability etkisi için net kontrol listesi.
Hosting Paketi Seçerken Yapılan 5 Hata ve Net Çözüm Rehberi
Hosting paketi seçerken yapılan 5 yaygın hatayı öğrenin: yanlış kaynak planlama, kontrol paneli beklentisi, yedekleme/SSL eksikleri ve daha fazlası.
Açık Portları Kapatma: Sunucu Hardening Rehberi
Açık portları kapatmak için net kontrol adımları: hangi portlar riskli, nasıl taranır, güvenli kapatma ve kalıcı hardening ayarları.