Was ist WebSocket — und warum ist es anders als HTTP?
HTTP folgt dem Request-Response-Modell: Client fragt, Server antwortet, Verbindung wird geschlossen. Das ist für statische Seiten perfekt, aber ein Problem, wenn der Server dem Client etwas mitteilen muss, ohne dass der Client aktiv nachgefragt hat.
Die historischen Workarounds waren:
- Polling: Client fragt alle X Sekunden den Server "Gibts was Neues?" — ineffizient, hoher Netzwerk-Overhead, Latenz zwischen Updates.
- Long Polling: Server hält die Verbindung offen, bis er Daten hat, dann antwortet er und der Client fragt sofort wieder an — besser als Polling, aber trotzdem viele Verbindungen und Reconnects.
- Server-Sent Events (SSE): Server sendet Events an Client (kein Reverse-Kanal) — gut für Notifications, aber kein Bidirectional Communication.
WebSocket löst das alles: Nach dem initialen HTTP-Handshake (Upgrade) bleibt die Verbindung offen — beide Seiten (Client und Server) können jederzeit Daten senden, ohne jedes Mal einen neuen Request zu starten. Das ist der entscheidende Unterschied zu HTTP.
Der WebSocket-Handshake: HTTP wird zum WebSocket
Ein WebSocket beginnt als normaler HTTP-Request mit einem Upgrade-Header. Der Server antwortet mit 101 Switching Protocols — und ab jetzt ist es ein WebSocket. Das ist der einzige Zeitpunkt, in dem der Server einen "push" an den Client macht: der Accept-Handshake.
Node.js WebSocket-Server:一步一步
Node.js ist die populärste Umgebung für WebSocket-Server — weil das Event-Modell von Node.js perfekt zur asynchronen Natur von WebSockets passt. Wir nutzen ws, die schlanke Standard-Bibliothek:
// npm install ws
const { WebSocketServer } = require('ws');
const wss = new WebSocketServer({ port: 8080 });
wss.on('connection', (ws, req) => {
const clientIp = req.socket.remoteAddress;
console.log(`Client verbunden: ${clientIp}`);
// Heartbeat: alle 30 Sekunden Ping senden
const pingInterval = setInterval(() => {
if (ws.readyState === ws.OPEN) {
ws.ping();
}
}, 30000);
ws.on('message', (data) => {
console.log(`Nachricht empfangen: ${data.toString()}`);
// Broadcast an alle verbundenen Clients
wss.clients.forEach((client) => {
if (client.readyState === client.OPEN) {
client.send(data.toString());
}
});
});
ws.on('close', () => {
clearInterval(pingInterval);
console.log('Client getrennt');
});
ws.on('error', (err) => {
console.error('WebSocket-Fehler:', err.message);
});
});
console.log('WebSocket-Server läuft auf Port 8080');
Das ist ein vollständiger Broadcast-Server: jede Nachricht, die ein Client sendet, wird an alle verbundenen Clients weitergeleitet — der Kern eines Chat-Systems.
Für produktive Nutzung brauchst du zusätzlich:
- Authentifizierung — der HTTP-Handshake ist der richtige Ort: prüfe ein JWT oder Session-Token bevor du die WebSocket-Verbindung akzeptierst.
- Message-Typen — definiere ein Protokoll (JSON mit
type-Feld), damit du Nachrichten unterscheiden kannst (Chat-Message, System-Notification, Typing-Indicator). - Reconnection-Strategie — Clients reconnecten automatisch, aber dein Server muss den Zustand (welche Channels hat der User?) beim Reconnect wiederherstellen.
- Rate Limiting — WebSockets können für DDoS missbraucht werden: limitiere Nachrichten pro Sekunde pro Client.
Nginx als Reverse Proxy für WebSockets
WebSockets funktionieren nicht direkt mit Nginx-Default-Konfiguration — du musst den Upgrade-Header explizit durchreichen:
# /etc/nginx/conf.d/websocket.conf
server {
listen 443 ssl;
server_name deine-domain.de;
# SSL-Konfiguration (certbot oder eigenes Zertifikat)
ssl_certificate /etc/letsencrypt/live/deine-domain.de/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/deine-domain.de/privkey.pem;
# WebSocket Upgrade Header
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header 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;
# Timeouts: WebSocket-Verbindungen bleiben lange offen
proxy_read_timeout 86400;
proxy_send_timeout 86400;
location /ws/ {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Normale HTTP-Routen
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
}
}
proxy_read_timeout muss hoch sein
WebSocket-Verbindungen bleiben oft stundenlang offen. Nginx' Default proxy_read_timeout von 60 Sekunden führt dazu, dass Nginx die Verbindung schliesst, obwohl Client und Backend noch kommunizieren. Setze diesen Wert auf mindestens 86400 (24 Stunden) oder höher, wenn du lange offene Verbindungen brauchst (z.B. Live-Trading-Dashboards).
Load Balancing für WebSockets
Ein einzelner WebSocket-Server ist ein Single Point of Failure — bei hohem Traffic auch ein Flaschenhals. Load Balancing mit WebSockets ist aber komplexer als bei HTTP, weil du sticky sessions brauchst: ein Client muss immer mit demselben Server verbunden bleiben, weil WebSocket-State (Channels, Subscriptions) serverseitig gespeichert wird.
| Load Balancer | Sticky Sessions | WebSocket-Support | Eignung |
|---|---|---|---|
| Nginx | IP-Hash oder Cookie-basiert | Ja (mit Upgrade-Header) | Gut für kleine/mittlere Setups |
| HAProxy | Source-IP / Cookie | Ja (tcp-mode) | Sehr gut, flexibel konfigurierbar |
| Cloudflare Load Balancer | Automatisch | Ja (Layer 4) | Einfach, gut für globale Verteilung |
| AWS ALB | Cookie-basiert (AWSALB) | Ja (WebSocket-Support nativ) | Gut für AWS-Umgebungen |
Sticky Sessions mit Nginx:
# Sticky Session via IP-Hash
upstream websocket_backend {
ip_hash;
server 10.0.1.10:8080;
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}
# Alternativ: Cookie-basierte Sticky Sessions
upstream websocket_backend {
sticky cookie srv_id expires=1h domain=.deine-domain.de path=/;
server 10.0.1.10:8080;
server 10.0.1.11:8080;
}
Socket.io: Wenn du Robustheit brauchst
Das ws-Modul ist Low-Level. Socket.io bietet Automatic Fallback (Long Polling bei fehlendem WebSocket-Support), Rooms und Namespaces, Auto-Reconnection, und Binary Support. Für die meisten Production-Anwendungen ist Socket.io die bessere Wahl:
// Server (Express + Socket.io)
const express = require('express');
const http = require('http');
const { Server } = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = new Server(server, {
cors: { origin: "https://deine-domain.de" },
pingTimeout: 60000,
pingInterval: 25000,
});
io.on('connection', (socket) => {
console.log(`User verbunden: ${socket.id}`);
// Authentifizierung (Token aus Handshake-Query)
const token = socket.handshake.auth.token;
if (!verifyToken(token)) {
socket.emit('error', { message: 'Unauthorized' });
return socket.disconnect();
}
// Room beitreten (z.B. für Chat-Kanäle)
socket.on('join-room', (roomId) => {
socket.join(roomId);
socket.to(roomId).emit('user-joined', { userId: socket.id });
});
// Private Nachricht
socket.on('private-message', ({ to, message }) => {
io.to(to).emit('private-message', {
from: socket.id,
message,
timestamp: new Date().toISOString(),
});
});
// Broadcast im Raum
socket.on('chat-message', ({ roomId, message }) => {
socket.to(roomId).emit('chat-message', {
from: socket.id,
message,
timestamp: new Date().toISOString(),
});
});
socket.on('disconnect', () => {
console.log(`User getrennt: ${socket.id}`);
});
});
server.listen(8080, () => console.log('Socket.io Server auf Port 8080'));
Python Alternative: FastAPI + WebSockets
Python mit FastAPI bietet hervorragenden WebSocket-Support mit async/await-Semantik:
# server.py — FastAPI + WebSockets
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from typing import List
app = FastAPI()
# Connection Manager
class ConnectionManager:
def __init__(self):
self.active_connections: List[WebSocket] = []
async def connect(self, websocket: WebSocket):
await websocket.accept()
self.active_connections.append(websocket)
def disconnect(self, websocket: WebSocket):
self.active_connections.remove(websocket)
async def broadcast(self, message: str):
for connection in self.active_connections:
await connection.send_text(message)
manager = ConnectionManager()
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await manager.connect(websocket)
try:
while True:
data = await websocket.receive_text()
# Broadcast an alle
await manager.broadcast(f"User sagte: {data}")
except WebSocketDisconnect:
manager.disconnect(websocket)
# uvicorn server:app --host 0.0.0.0 --port 8080
Security: WebSocket-Absicherung
WebSockets eröffnen Angriffsvektoren, die bei normalem HTTP nicht existieren:
| Bedrohung | Gegenmassnahme |
|---|---|
| WebSocket Injection | Eingehende Nachrichten als Text behandeln, niemals als Code ausführen. Input-Validierung, Sanitization. |
| Cross-Site WebSocket Hijacking (CSWSH) | Origin-Header prüfen, CSRF-Token im Handshake, SameSite-Cookies. |
| DoS via Connection Flooding | Rate Limiting (max X Verbindungen pro IP), Connection-Limit konfigurieren. |
| Message Flooding | Max Messages/Sekunde pro Client. Queue-Grössen begrenzen. |
| Man-in-the-Middle | Nur WSS (WebSocket over TLS) erlauben. Nie unverschlüsselte ws:// in Produktion. |
WSS ist nicht optional — es ist Pflicht
Unverschlüsselte ws://-Verbindungen können auf Network-Ebene abgehört werden. Jeder WebSocket-Server in Produktion muss über HTTPS/WSS erreichbar sein. Das Zertifikat bekommst du kostenlos via Let's Encrypt (certbot), und Nginx übernimmt die TLS-Terminierung transparent für den WebSocket-Proxy.
Hosting-Anforderungen für WebSocket-Server
WebSocket-Server haben andere Ressourcen-Profile als HTTP-Server:
VPS-Mindestanforderungen: 1 GB RAM, 1 vCPU. Node.js WebSocket-Server mit 10.000 gleichzeitigen Verbindungen brauchen ca. 200–400 MB RAM. Bei 100.000+ Verbindungen brauchst du Horizontal Scaling (mehrere Server + Load Balancer) oder einen Managed Service wie Pusher, Ably, oder AWS API Gateway WebSocket.
Ports: WebSocket-Server laufen typischerweise auf einem separaten Port (z.B. 8080), nicht auf Port 80/443 direkt. Nginx auf 443 leitet an diesen Port weiter.
Process Manager: Nutze PM2 (Node.js) oder systemd, um den WebSocket-Server bei Crash automatisch neu zu starten und Logs zu verwalten.
Fazit: WebSocket ist einfach, Production-Ready aber nicht
Die WebSocket-API selbst ist simpel — ein Chat-Server in 50 Zeilen Node.js. Das Schwierige ist alles drumherum: Authentication, Reconnection, Message-Protokoll, Load Balancing, Security, Monitoring. Plane diese Architektur von Anfang an, nicht als Nachgedanke.
Für die meisten Projekte: Node.js + Socket.io + PM2 + Nginx. Die Stack-Selektion ist gut dokumentiert, die Community ist gross, und du kannst auf einem einzelnen VPS starten und bei Bedarf horizontal skalieren.
Wenn du keine Lust auf Server-Management hast: Managed WebSocket-Services (Pusher, Ably, FastAPI + Railway) eliminieren den Ops-Aufwand komplett — bei einem Aufpreis, der bei kleinem Massstab gering ist, aber bei hohem Traffic teuer werden kann.
VPS-Hosting für deinen WebSocket-Server
Root-Zugang, TCP-Port-Freiheit, skalierbare Server — perfekt für WebSocket-Anwendungen und Echtzeit-Features.
Zum Hosting-Vergleich