Modern web uygulamalarının vazgeçilmezi haline gelen WebSocket teknolojisi, gerçek zamanlı iletişim için mükemmel bir çözüm sunuyor. Ancak Apache arkasında bir WebSocket sunucusu çalıştırıyorsanız ve bu bağlantıları doğru şekilde proxy’lemek istiyorsanız, birkaç kritik yapılandırma adımını atlamanız durumunda ciddi sorunlarla karşılaşabilirsiniz. Bu yazıda Apache üzerinde WebSocket proxy yapılandırmasını en ince ayrıntısına kadar ele alacağız.
WebSocket ve Apache Proxy: Temel Kavramlar
WebSocket, HTTP’nin aksine kalıcı, çift yönlü bir bağlantı protokolüdür. Normal bir HTTP isteğinde istemci bir şey sorar, sunucu cevap verir ve bağlantı kapanır. WebSocket’te ise bağlantı açık kalır ve her iki taraf da istediği zaman veri gönderebilir. Chat uygulamaları, canlı bildirimler, oyunlar, finansal ticker’lar bunların hepsi WebSocket kullanır.
Apache, varsayılan olarak HTTP/1.1 upgrade mekanizmasını otomatik olarak işleyemez. Bu yüzden mod_proxy_wstunnel modülünü devreye almamız gerekiyor. Bu modül, gelen WebSocket bağlantılarını (ws:// veya wss://) arka plandaki gerçek WebSocket sunucusuna iletmekten sorumludur.
Gerçek dünya senaryomuz şu: Elimizde Node.js ile yazılmış bir chat uygulaması var, 3000 portunda çalışıyor ve WebSocket bağlantıları kabul ediyor. Biz bu uygulamayı Apache’nin arkasına koyacağız ve dışarıya sadece 443 (HTTPS/WSS) portu üzerinden hizmet vereceğiz.
Gerekli Modüllerin Aktifleştirilmesi
Öncelikle Apache’de hangi modüllerin etkin olduğunu kontrol edelim:
apache2ctl -M | grep -E "proxy|rewrite|ssl"
WebSocket proxy için aşağıdaki modüllere ihtiyacımız var:
- mod_proxy: Temel proxy işlevselliği
- mod_proxy_http: HTTP proxy desteği
- mod_proxy_wstunnel: WebSocket tünelleme (en önemli olan bu)
- mod_rewrite: URL yeniden yazma
- mod_ssl: HTTPS/WSS desteği için
Ubuntu/Debian üzerinde bu modülleri aktifleştirelim:
sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enmod proxy_wstunnel
sudo a2enmod rewrite
sudo a2enmod ssl
sudo a2enmod headers
sudo systemctl restart apache2
CentOS/RHEL sistemlerde modüller genellikle httpd.conf veya /etc/httpd/conf.modules.d/ altındaki dosyalarda tanımlanır:
# /etc/httpd/conf.modules.d/00-proxy.conf dosyasini kontrol edin
grep -n "proxy_wstunnel" /etc/httpd/conf.modules.d/00-proxy.conf
# Satir yorumlanmissa (#) onundeki # isaretini kaldirin
sudo sed -i 's/#LoadModule proxy_wstunnel_module/LoadModule proxy_wstunnel_module/' /etc/httpd/conf.modules.d/00-proxy.conf
sudo systemctl restart httpd
Temel WebSocket Proxy Yapılandırması
İlk olarak basit bir HTTP üzerinden WebSocket proxy kurulumunu görelim. Bu yapıyı test ortamlarında veya Apache’nin önünde başka bir SSL sonlandırıcı (örneğin bir load balancer) olduğu durumlarda kullanabilirsiniz:
<VirtualHost *:80>
ServerName chat.orneksite.com
# Proxy ayarlari
ProxyRequests Off
ProxyPreserveHost On
# WebSocket baglantilari icin wss/ws proxy
RewriteEngine On
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/?(.*) "ws://localhost:3000/$1" [P,L]
# Normal HTTP istekleri icin proxy
ProxyPass / http://localhost:3000/
ProxyPassReverse / http://localhost:3000/
ErrorLog ${APACHE_LOG_DIR}/chat-error.log
CustomLog ${APACHE_LOG_DIR}/chat-access.log combined
</VirtualHost>
Burada kritik nokta şu: RewriteRule direktifleri ProxyPass direktiflerinden önce gelmelidir. Apache kuralları sırayla işler ve WebSocket upgrade isteği önce yakalanmalıdır. Eğer ProxyPass önce gelirse, Apache WebSocket bağlantısını normal HTTP trafiği gibi işlemeye çalışır ve bağlantı başarısız olur.
HTTPS/WSS ile Güvenli Yapılandırma
Prodüksiyon ortamında kesinlikle SSL kullanmalısınız. WSS (WebSocket Secure), WS’nin TLS üzerinden çalışan versiyonudur. Apache SSL’i sonlandırır ve arka planla HTTP üzerinden konuşur:
<VirtualHost *:443>
ServerName chat.orneksite.com
# SSL Ayarlari
SSLEngine On
SSLCertificateFile /etc/letsencrypt/live/chat.orneksite.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/chat.orneksite.com/privkey.pem
# Guvenlik headerlari
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
ProxyRequests Off
ProxyPreserveHost On
# SSL proxy ayari - arka plan sunucusu HTTP kullaniyorsa
SSLProxyEngine Off
RewriteEngine On
# WebSocket upgrade isteklerini yonlendir
RewriteCond %{HTTP:Upgrade} ^websocket$ [NC]
RewriteCond %{HTTP:Connection} ^(.*,s*)?[Uu]pgrade(,.*)?$ [NC]
RewriteRule ^/socket/(.*) ws://localhost:3000/socket/$1 [P,L]
# Normal HTTP istekleri
ProxyPass /socket/ http://localhost:3000/socket/
ProxyPassReverse /socket/ http://localhost:3000/socket/
# Diger istekler icin ayri backend
ProxyPass / http://localhost:8080/
ProxyPassReverse / http://localhost:8080/
ErrorLog ${APACHE_LOG_DIR}/chat-ssl-error.log
CustomLog ${APACHE_LOG_DIR}/chat-ssl-access.log combined
</VirtualHost>
# HTTP'yi HTTPS'e yonlendir
<VirtualHost *:80>
ServerName chat.orneksite.com
RewriteEngine On
RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]
</VirtualHost>
Belirli Path Üzerinden WebSocket Proxy
Gerçek dünyada sıkça karşılaşılan senaryo şu: Ana siteniz orneksite.com üzerinde çalışıyor ama WebSocket bağlantıları sadece /ws ya da /socket.io gibi belirli bir path üzerinden geliyor. Socket.io kullanan bir uygulama için yapılandırma şu şekilde olmalı:
<VirtualHost *:443>
ServerName orneksite.com
SSLEngine On
SSLCertificateFile /etc/ssl/certs/orneksite.crt
SSLCertificateKeyFile /etc/ssl/private/orneksite.key
ProxyRequests Off
ProxyPreserveHost On
RewriteEngine On
# Socket.io polling (HTTP long-polling fallback)
RewriteCond %{QUERY_STRING} transport=polling
RewriteRule /socket.io/(.*) http://localhost:3000/socket.io/$1 [P,L]
# Socket.io WebSocket upgrade
RewriteCond %{HTTP:Upgrade} ^websocket$ [NC]
RewriteRule /socket.io/(.*) ws://localhost:3000/socket.io/$1 [P,L]
# Socket.io genel proxy
ProxyPass /socket.io/ http://localhost:3000/socket.io/
ProxyPassReverse /socket.io/ http://localhost.3000/socket.io/
# Ana uygulama
ProxyPass / http://localhost:8080/
ProxyPassReverse / http://localhost:8080/
# Onemli: ProxyPass timeout ayarlari
ProxyTimeout 3600
ErrorLog ${APACHE_LOG_DIR}/orneksite-error.log
CustomLog ${APACHE_LOG_DIR}/orneksite-access.log combined
</VirtualHost>
ProxyTimeout değerini yüksek tutmak çok önemli. Varsayılan değer genellikle 60 saniyedir ve WebSocket bağlantısı 60 saniye boyunca herhangi bir veri alışverişi olmazsa Apache bağlantıyı keser. Chat uygulamalarında kullanıcılar saatlerce bir şey yazmayabilir, bu yüzden bu değeri 3600 (1 saat) veya daha yüksek ayarlamanız gerekiyor.
Load Balancer Arkasında WebSocket Proxy
Birden fazla WebSocket sunucunuz varsa Apache’yi bir load balancer olarak da kullanabilirsiniz. Ancak WebSocket için sticky session (yapışkan oturum) zorunludur. Bir kullanıcının WebSocket bağlantısı kurulduğunda, sonraki tüm istekler aynı backend sunucusuna gitmek zorundadır:
<VirtualHost *:443>
ServerName ws.orneksite.com
SSLEngine On
SSLCertificateFile /etc/ssl/certs/ws-orneksite.crt
SSLCertificateKeyFile /etc/ssl/private/ws-orneksite.key
ProxyRequests Off
# WebSocket sunucu kümesi - sticky session ile
<Proxy balancer://wscluster>
BalancerMember ws://ws1.internal:3000 route=ws1
BalancerMember ws://ws2.internal:3000 route=ws2
BalancerMember ws://ws3.internal:3000 route=ws3
ProxySet lbmethod=byrequests
ProxySet stickysession=ROUTEID
</Proxy>
<Proxy balancer://httpcluster>
BalancerMember http://ws1.internal:3000 route=ws1
BalancerMember http://ws2.internal:3000 route=ws2
BalancerMember http://ws3.internal:3000 route=ws3
ProxySet lbmethod=byrequests
ProxySet stickysession=ROUTEID
</Proxy>
RewriteEngine On
RewriteCond %{HTTP:Upgrade} ^websocket$ [NC]
RewriteCond %{HTTP:Connection} ^(.*,s*)?[Uu]pgrade(,.*)?$ [NC]
RewriteRule ^/(.*) balancer://wscluster/$1 [P,L]
ProxyPass / balancer://httpcluster/
ProxyPassReverse / balancer://httpcluster/
# Cookie uzerinden sticky session
Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
ProxyTimeout 3600
ErrorLog ${APACHE_LOG_DIR}/ws-lb-error.log
CustomLog ${APACHE_LOG_DIR}/ws-lb-access.log combined
</VirtualHost>
Yaygın Sorunlar ve Çözümleri
400 Bad Request Hatası
Bu en sık karşılaşılan sorundur. WebSocket el sıkışması (handshake) sırasında Apache doğru başlıkları iletmiyordur. mod_proxy_wstunnel yüklü olmadığında veya RewriteRule’lar yanlış sıralandığında bu hata alınır. Kontrol etmek için:
# Apache error logunu canli izle
sudo tail -f /var/log/apache2/error.log
# WebSocket el sikismasini test et (wscat gerekli)
npm install -g wscat
wscat -c wss://chat.orneksite.com/socket
# HTTP upgrade basliklarini kontrol et
curl -i -N
-H "Connection: Upgrade"
-H "Upgrade: websocket"
-H "Sec-WebSocket-Version: 13"
-H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ=="
https://chat.orneksite.com/socket
Bağlantı Zaman Aşımı Sorunları
WebSocket bağlantıları uzun süre açık kalabilir. Sistem seviyesinde de bazı ayarlar gerekebilir:
# Apache'nin KeepAlive ayarlarini kontrol et
grep -r "KeepAlive|Timeout" /etc/apache2/apache2.conf
# /etc/apache2/apache2.conf veya ilgili vhost dosyasina ekle
# KeepAliveTimeout 65
# Timeout 3600
# TCP keepalive ayarlari - uzun surecli WS baglantilari icin
echo 'net.ipv4.tcp_keepalive_time = 600' >> /etc/sysctl.conf
echo 'net.ipv4.tcp_keepalive_intvl = 60' >> /etc/sysctl.conf
echo 'net.ipv4.tcp_keepalive_probes = 10' >> /etc/sysctl.conf
sysctl -p
mod_proxy_wstunnel Yüklü Olup Olmadığını Doğrulama
# Modülün yüklü olup olmadigini kesin olarak dogrula
apache2ctl -M 2>/dev/null | grep -i "proxy_wstunnel"
# Cikti bu olmali:
# proxy_wstunnel_module (shared)
# Yoksa yukle
sudo a2enmod proxy_wstunnel
sudo systemctl reload apache2
# Apache'nin WebSocket baglantisini nasil isledigini gormek icin debug log
sudo sed -i 's/LogLevel warn/LogLevel debug/' /etc/apache2/apache2.conf
sudo systemctl reload apache2
sudo tail -f /var/log/apache2/error.log | grep -i websocket
Güvenlik Yapılandırması
WebSocket proxy kurulurken güvenliği de göz önünde bulundurmalısınız. Açık proxy olmamasına dikkat edin ve sadece belirlediğiniz backend’lere izin verin:
<VirtualHost *:443>
ServerName chat.orneksite.com
SSLEngine On
SSLCertificateFile /etc/letsencrypt/live/chat.orneksite.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/chat.orneksite.com/privkey.pem
# Acik proxy'yi devre disi birak
ProxyRequests Off
# Sadece belirli originlere izin ver
Header always set Access-Control-Allow-Origin "https://chat.orneksite.com"
# Guvenlik headerlari
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-XSS-Protection "1; mode=block"
Header always set X-Content-Type-Options "nosniff"
# Rate limiting icin mod_ratelimit veya mod_evasive kullanin
# Cok sayida WS baglantisi acmaya calisan IP'leri engelle
<Location /socket>
# Ayni IP'den max 100 eszamanli baglanti
SetEnvIf Remote_Addr "^(.*)$" CLIENT_IP=$1
</Location>
RewriteEngine On
# Sadece izin verilen originlerden gelen WS isteklerini kabul et
RewriteCond %{HTTP:Origin} !^https://chat.orneksite.com$ [NC]
RewriteCond %{HTTP:Upgrade} ^websocket$ [NC]
RewriteRule .* - [F,L]
RewriteCond %{HTTP:Upgrade} ^websocket$ [NC]
RewriteCond %{HTTP:Connection} ^(.*,s*)?[Uu]pgrade(,.*)?$ [NC]
RewriteRule ^/socket/(.*) ws://localhost:3000/socket/$1 [P,L]
ProxyPass /socket/ http://localhost:3000/socket/
ProxyPassReverse /socket/ http://localhost:3000/socket/
ProxyTimeout 3600
</VirtualHost>
Yapılandırmayı Test Etme ve İzleme
Kurulumu tamamladıktan sonra her şeyin doğru çalıştığını doğrulamanız gerekiyor:
# Yapilandirma sozdizimi kontrolu
sudo apache2ctl configtest
# Cikti: Syntax OK olmali
# Servisi yeniden yükle
sudo systemctl reload apache2
# WebSocket baglantisini test et
wscat -c wss://chat.orneksite.com/socket
# Baglanti kuruldugunda JSON mesaj gonder
# {"type": "ping"}
# Apache durumunu izle - mod_status ile
# Oncelikle mod_status'u aktif edin
sudo a2enmod status
# /etc/apache2/mods-enabled/status.conf dosyasini duzenle
# ExtendedStatus On ekle
# Canli izleme
watch -n 2 'curl -s http://localhost/server-status?auto | grep -E "BusyWorkers|IdleWorkers|Uptime"'
# WebSocket baglanti sayisini izle
ss -tnp | grep apache2 | wc -l
Logları düzenli olarak izlemek için bir alias eklemek de hayatı kolaylaştırır:
# ~/.bashrc veya ~/.bash_aliases dosyasina ekle
alias ws-log='sudo tail -f /var/log/apache2/chat-error.log | grep -E "websocket|proxy|tunnel|upgrade"'
alias ws-conn='watch -n 5 "ss -tnp | grep -E "3000|ESTAB" | wc -l"'
source ~/.bashrc
Performans Optimizasyonu
Yüksek trafikli WebSocket uygulamalarında Apache worker MPM veya event MPM kullanmalısınız. Prefork MPM WebSocket için uygun değildir çünkü her bağlantı için bir process oluşturur:
# Mevcut MPM'i kontrol et
apache2ctl -V | grep MPM
# Event MPM'e gec (en iyi performans)
sudo a2dismod mpm_prefork
sudo a2enmod mpm_event
sudo systemctl restart apache2
# /etc/apache2/mods-available/mpm_event.conf
# <IfModule mpm_event_module>
# StartServers 2
# MinSpareThreads 25
# MaxSpareThreads 75
# ThreadLimit 64
# ThreadsPerChild 25
# MaxRequestWorkers 400
# MaxConnectionsPerChild 0
# </IfModule>
Sonuç
Apache ile WebSocket proxy yapılandırması ilk bakışta karmaşık görünse de temel prensipler anlaşıldığında oldukça yönetilebilir hale geliyor. Özetlemek gerekirse en kritik noktalar şunlar:
- mod_proxy_wstunnel mutlaka yüklü ve aktif olmalı
- RewriteRule direktifleri
ProxyPass‘tan önce gelmeli - ProxyTimeout değeri WebSocket uygulamanızın kullanım senaryosuna göre ayarlanmalı
- Prodüksiyonda mutlaka WSS (WebSocket Secure) kullanılmalı
- Yüksek bağlantı sayısı için event MPM tercih edilmeli
- Load balancer senaryolarında sticky session zorunlu
En sık yapılan hata, yapılandırmanın tarayıcıda test edilmesi ama wscat gibi araçlarla gerçek WebSocket el sıkışmasının denenmemesidir. Bir sorunla karşılaştığınızda her zaman Apache error logunu debug seviyesinde incelemeye başlayın ve wscat ile doğrudan bağlantı testi yapın. Sorunların büyük çoğunluğu ya yüklü olmayan modülden ya da yanlış sıralanmış RewriteRule direktiflerinden kaynaklanıyor.