Das Proxying von WebSocket-Verbindungen erfordert die Konfiguration eines Vermittlungsservers, um den HTTP Upgrade-Handshake korrekt zu verarbeiten und die persistente, Vollduplex-TCP-Verbindung aufrechtzuerhalten, die für die WebSocket-Kommunikation erforderlich ist. Diese Einrichtung ermöglicht Funktionen wie Lastverteilung, SSL-Terminierung, Caching und Zugriffskontrolle für Echtzeitanwendungen.
WebSocket-Proxying verstehen
WebSockets stellen eine persistente Verbindung zwischen einem Client und einem Server über eine einzige TCP-Verbindung her. Dies unterscheidet sich von traditionellem HTTP, das typischerweise kurzlebige Anfrage-/Antwortzyklen verwendet. Der Übergang von HTTP zu WebSocket wird über einen "Upgrade"-Mechanismus im HTTP/1.1-Protokoll initiiert.
Der Client sendet eine initiale HTTP GET-Anfrage mit spezifischen Headern:
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Key: [base64-encoded nonce]
* Sec-WebSocket-Version: 13
Wenn der Server WebSockets unterstützt, antwortet er mit einem HTTP 101 Switching Protocols-Status, der das Upgrade bestätigt:
* HTTP/1.1 101 Switching Protocols
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Accept: [response key derived from client's key]
Nach diesem Handshake ist die Verbindung kein HTTP mehr, sondern ein roher TCP-Socket, über den WebSocket-Frames ausgetauscht werden. Ein Proxy-Server muss so konfiguriert werden, dass er diese Upgrade- und Connection-Header korrekt weiterleitet und dann die langlebige TCP-Verbindung ohne Pufferung oder vorzeitiges Schließen aufrechterhält.
Einrichtung für gängige Proxy-Server
Nginx
Nginx kann WebSocket-Verbindungen effektiv proxieren, indem es den HTTP Upgrade-Handshake verarbeitet. Der Schlüssel liegt darin, die Upgrade- und Connection-Header vom Client an den Backend-Server weiterzuleiten und sicherzustellen, dass HTTP/1.1 verwendet wird.
http {
upstream websocket_backend {
server backend_server_1:8080;
server backend_server_2:8080;
}
server {
listen 80;
server_name yourdomain.com;
location /ws/ {
proxy_pass http://websocket_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host; # Important for backend if it relies on host header
proxy_read_timeout 86400s; # Adjust as needed for long-lived connections
proxy_send_timeout 86400s;
proxy_buffering off; # Disable buffering for real-time data
}
# Other HTTP locations
location / {
proxy_pass http://other_http_backend;
# ... other HTTP proxy settings
}
}
}
proxy_http_version 1.1;: Wesentlich für denUpgrade-Header-Mechanismus.proxy_set_header Upgrade $http_upgrade;: Leitet denUpgrade-Header des Clients weiter.proxy_set_header Connection "upgrade";: Leitet denConnection-Header des Clients weiter. Nginx handhabt die Transformation vonConnection: upgradezuConnection: keep-alivefür HTTP/1.1-Verbindungen automatisch, wenn nicht explizit auf "upgrade" gesetzt. Das explizite Setzen stellt das korrekte Verhalten für WebSockets sicher.proxy_read_timeout/proxy_send_timeout: Erhöhen Sie diese gegenüber den Standardwerten, um zu verhindern, dass Nginx langlebige WebSocket-Verbindungen aufgrund von Inaktivität schließt.proxy_buffering off;: Verhindert, dass Nginx Antworten puffert, was für die Echtzeit-WebSocket-Kommunikation entscheidend ist, um minimale Latenz zu gewährleisten.
Apache HTTP Server
Apache kann WebSocket-Verbindungen mit mod_proxy_wstunnel proxieren, das Teil von mod_proxy ist. Stellen Sie sicher, dass mod_proxy, mod_proxy_http und mod_proxy_wstunnel aktiviert sind.
<VirtualHost *:80>
ServerName yourdomain.com
# Enable proxy modules
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
# Proxy WebSocket connections
<Location /ws/>
ProxyPass ws://backend_server:8080/ws/
ProxyPassReverse ws://backend_server:8080/ws/
</Location>
# Proxy secure WebSocket connections (WSS)
<Location /wss/>
ProxyPass wss://backend_server:8443/wss/
ProxyPassReverse wss://backend_server:8443/wss/
</Location>
# Other HTTP locations
<Location />
ProxyPass http://other_http_backend/
ProxyPassReverse http://other_http_backend/
</Location>
</VirtualHost>
ProxyPass ws://...: Dasws://-Schema weistmod_proxy_wstunnelan, das WebSocket-Upgrade zu handhaben. Für sichere WebSockets verwenden Siewss://.ProxyPassReverse: Stellt die korrekte URL-Umschreibung in den Antwort-Headern sicher.- Apaches
mod_proxyhandhabt dieUpgrade- undConnection-Header automatisch, wennws://oderwss://angegeben ist.
HAProxy
HAProxy unterstützt WebSocket-Proxying seit Version 1.5. Es kann im http-Modus zur Überprüfung von Headern oder im tcp-Modus zur reinen Weiterleitung betrieben werden. Für WebSockets ist es üblich, den http-Modus mit spezifischen Regeln zur Erkennung der Upgrade-Anfrage zu verwenden.
global
log /dev/log local0 info
maxconn 4000
user haproxy
group haproxy
daemon
defaults
mode http
log global
option httplog
option dontlognull
timeout connect 5000ms
timeout client 50000ms # Increased for client-side WebSocket connections
timeout server 50000ms # Increased for backend-side WebSocket connections
frontend http_frontend
bind *:80
acl is_websocket hdr(Upgrade) -i websocket
use_backend ws_backend if is_websocket
default_backend http_backend
backend ws_backend
mode http
# This option is crucial for WebSockets in HTTP mode; it tells HAProxy to
# switch to TCP tunneling after the HTTP upgrade.
option http-tunnel
server backend_ws_1 backend_server_1:8080 check
server backend_ws_2 backend_server_2:8080 check
backend http_backend
mode http
server backend_http_1 backend_server_3:80 check
acl is_websocket hdr(Upgrade) -i websocket: Definiert eine Access Control List (ACL), die Anfragen mit einemUpgrade: websocket-Header (Groß-/Kleinschreibung ignorierend) abgleicht.use_backend ws_backend if is_websocket: Leitet WebSocket-Upgrade-Anfragen an dasws_backendweiter.option http-tunnel: Dies ist entscheidend. Wenn HAProxy einenUpgrade-Header erkennt und diese Option gesetzt ist, wechselt es nach dem initialen HTTP-Handshake vom HTTP-Parsing-Modus zu einem rohen TCP-Tunnel. Dies ermöglicht dem WebSocket-Protokoll, ungehindert zu arbeiten.timeout client/timeout server: Diese Timeouts sollten für WebSocket-Backends erheblich erhöht werden, um zu verhindern, dass HAProxy langlebige Verbindungen schließt.
Behebung häufiger Probleme
Verbindungsabbruch oder Timeout
- Proxy-Timeouts: Die häufigste Ursache. Stellen Sie sicher, dass
proxy_read_timeout,proxy_send_timeout(Nginx),timeout client,timeout server(HAProxy) oder gleichwertige Einstellungen in anderen Proxys für WebSocket-Verbindungen ausreichend hoch sind (z. B. 24 Stunden oder mehr). - Inaktive Verbindungen: Einige Proxys oder Netzwerkgeräte könnten inaktive TCP-Verbindungen aggressiv schließen. Wenn das WebSocket-Protokoll einen Keep-Alive-Mechanismus (z. B. Ping/Pong-Frames) hat, stellen Sie sicher, dass dieser sowohl auf Client- als auch auf Serverseite für das Senden periodischer Pings konfiguriert ist.
- Inaktivität des Load Balancers: Wenn ein Load Balancer vor dem Proxy sitzt, könnte dieser auch eigene Inaktivitäts-Timeouts haben. Überprüfen Sie diese Einstellungen.
HTTP 400 Bad Request (Upgrade-Header fehlt oder ist falsch)
- Client-Anfrage: Überprüfen Sie, ob der Client die Header
Upgrade: websocketundConnection: Upgradekorrekt sendet. - Proxy-Konfiguration: Stellen Sie sicher, dass der Proxy so konfiguriert ist, dass er diese Header an das Backend weiterleitet.
- Nginx: Überprüfen Sie
proxy_set_header Upgrade $http_upgrade;undproxy_set_header Connection "upgrade";. - Apache: Stellen Sie sicher, dass
mod_proxy_wstunnelaktiviert ist undProxyPassws://oderwss://verwendet. - HAProxy: Bestätigen Sie, dass die
acl is_websocketdenUpgrade-Header korrekt identifiziert unduse_backendihn an ein Backend mitoption http-tunnelleitet.
- Nginx: Überprüfen Sie
HTTP 502 Bad Gateway
- Probleme mit dem Backend-Server: Der Proxy hat eine ungültige Antwort vom Backend erhalten.
- Läuft der Backend-WebSocket-Server und lauscht er am erwarteten Port?
- Ist das Backend korrekt für die Handhabung von WebSocket-Upgrades konfiguriert?
- Überprüfen Sie die Backend-Server-Logs auf Fehler.
- Netzwerkkonnektivität: Überprüfen Sie, ob der Proxy den Backend-Server am angegebenen Port erreichen kann.
- Proxy-Pufferung: Obwohl
proxy_buffering offim Allgemeinen gut für WebSockets ist, können übermäßige Daten oder Fehlkonfigurationen manchmal zu 502-Fehlern führen, wenn der Proxy Schwierigkeiten hat, den Stream zu verarbeiten.
Sicherheitsaspekte (SSL/TLS-Terminierung)
- WSS (Sichere WebSockets): Für sichere WebSocket-Verbindungen (wss://) muss der Proxy die SSL/TLS-Terminierung handhaben.
- Der Proxy lauscht auf Port 443 (oder einem anderen sicheren Port), führt den TLS-Handshake durch und leitet dann den entschlüsselten WebSocket-Verkehr an das Backend weiter (oft intern über einfaches
ws://oderhttp://). - Beispiel Nginx für WSS:
```nginx
server {
listen 443 ssl;
server_name yourdomain.com;
ssl_certificate /etc/nginx/ssl/yourdomain.crt;
ssl_certificate_key /etc/nginx/ssl/yourdomain.key;location /ws/ { proxy_pass http://websocket_backend; # Backend might be plain HTTP proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_read_timeout 86400s; proxy_send_timeout 86400s; proxy_buffering off; }}
`` * **Origin-Header:** WebSocket-Clients senden einenOrigin`-Header. Backend-Server validieren diesen Header oft aus Sicherheitsgründen. Stellen Sie sicher, dass Ihr Proxy diesen Header nicht entfernt oder ändert, wenn das Backend darauf angewiesen ist.
- Der Proxy lauscht auf Port 443 (oder einem anderen sicheren Port), führt den TLS-Handshake durch und leitet dann den entschlüsselten WebSocket-Verkehr an das Backend weiter (oft intern über einfaches
Proxy-Puffergrenzen
- Nginx:
proxy_buffering off;ist entscheidend. Wenn nicht gesetzt, könnte Nginx große WebSocket-Nachrichten puffern, was Latenz einführt oder Timeouts verursacht. - HAProxy:
option http-tunnelhandhabt dies, indem es auf rohes TCP umschaltet.
Vergleich von Proxy-Servern für WebSockets
| Feature | Nginx | Apache HTTP Server (mod_proxy_wstunnel) | HAProxy |
|---|---|---|---|
| Konfiguration | Manuelle Header-Weiterleitung | Spezifische ws:// / wss:// Direktiven |
ACLs und option http-tunnel |
| Komplexität | Moderat | Moderat | Moderat bis Hoch (für erweiterte Funktionen) |
| Performance | Hoch (Ereignisgesteuert) | Moderat (Prozess-/Thread-basiert) | Hoch (Ereignisgesteuert) |
| SSL/TLS-Terminierung | Exzellent | Gut | Exzellent |
| Lastverteilung | Round-robin, IP-Hash, least_conn | Round-robin | Umfangreich (viele Algorithmen, Health Checks) |
| Sticky Sessions | IP-Hash (für Basic), Drittanbieter-Module | Begrenzt | Cookie-basiert, Quell-IP |
| Pufferkontrolle | proxy_buffering off |
Von mod_proxy_wstunnel gehandhabt |
option http-tunnel deaktiviert HTTP-Pufferung |
Wichtige Überlegungen
- Sticky Sessions: Für einige WebSocket-Anwendungen muss ein Client möglicherweise während seiner gesamten Sitzung mit demselben Backend-Server verbunden bleiben (z. B. wenn serverseitiger Zustand beibehalten wird).
- Nginx: Verwenden Sie
ip_hashimupstream-Block. - HAProxy: Verwenden Sie
balance sourceodercookie-Direktiven. - Stellen Sie sicher, dass Ihre Backend-Anwendung für horizontale Skalierung ohne die Notwendigkeit von Sticky Sessions ausgelegt ist, wenn möglich, da dies die Lastverteilung vereinfacht.
- Nginx: Verwenden Sie
- Health Checks: Konfigurieren Sie robuste Health Checks für Ihre Backend-WebSocket-Server innerhalb des Proxys. Dies stellt sicher, dass nur gesunde Server WebSocket-Upgrade-Anfragen erhalten.
- Logging: Konfigurieren Sie eine umfassende Protokollierung auf dem Proxy und dem Backend. WebSocket-Verbindungsprobleme können vorübergehend sein, und detaillierte Protokolle sind für die Diagnose unerlässlich.
- HTTP/2 und WebSockets: Obwohl WebSockets typischerweise HTTP/1.1 für den Upgrade-Handshake verwenden, können sie mit HTTP/2 koexistieren. Das WebSocket-Protokoll selbst läuft jedoch nicht direkt über HTTP/2-Frames. Proxys, die HTTP/2 von Clients verarbeiten, werden typischerweise auf HTTP/1.1 für das WebSocket-Upgrade zum Backend downgraden.