Websocket Sticky Sessions
title: "WebSocket Sticky Sessions" description: "Understand sticky sessions for WebSocket load balancing, why they are needed, and how to configure them with different proxy solutions." weight: 20 date: 2026-06-28 lastmod: 2026-06-28 tags: ["apis", "websocket"]
Sticky sessions (session affinity) ensure that all WebSocket messages from a client go to the same server instance. This is critical for stateful WebSocket applications running behind a load balancer.
## What You'll Learn
- Why WebSocket needs sticky sessions
- Sticky session configuration in NGINX
- Sticky sessions with HAProxy
- Cookie-based vs IP-based affinity
- Alternatives to sticky sessions
## Why It Matters
Without sticky sessions, load balancers may route consecutive WebSocket frames from the same client to different servers, breaking the connection. Understanding sticky sessions is essential for scaling WebSocket applications.
## Real-World Use
A multiplayer gaming platform uses NGINX with sticky sessions to route WebSocket connections. Each game session is tied to a specific server, ensuring consistent state and low-latency communication for all players in a match.
## Flow Chart
```mermaid
flowchart LR
A[Client] --> B[Load Balancer]
B --> C{Sticky Session?}
C -->|Yes| D[Server A]
C -->|Yes| E[Server A]
C -->|No| F[Server A / Server B]
D --> G[Consistent State]
F --> H[State Loss]
Code Examples
Example 1: NGINX Sticky Sessions for WebSocket
upstream websocket_backend {
# Sticky session using cookies
sticky cookie srv_id expires=1h path=/;
server backend1.example.com:8080;
server backend2.example.com:8080;
server backend3.example.com:8080;
}
server {
listen 443 ssl;
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 "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# Timeout settings for long-lived connections
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
}
Expected output: NGINX routes WebSocket connections to the same backend using a sticky cookie, with long timeouts for persistent connections.
Example 2: HAProxy Sticky Sessions
frontend ws_frontend
bind *:443 ssl crt /etc/ssl/certs/server.pem
mode tcp
default_backend ws_backend
backend ws_backend
mode tcp
# Sticky session based on source IP
balance source
hash-type consistent
# Alternative: cookie-based stickiness
# cookie SERVERID insert indirect nocache
server backend1 backend1:8080 check
server backend2 backend2:8080 check
server backend3 backend3:8080 check
# Health check for WebSocket
option httpchk GET /health
http-check expect status 200
Expected output: HAProxy uses source IP-based stickiness to route WebSocket connections consistently, with consistent hashing for minimal redistribution on server changes.
Example 3: Application-Level Sticky Sessions with Redis
const WebSocket = require('ws');
const Redis = require('ioredis');
class StickySessionManager {
constructor() {
this.redis = new Redis();
this.sessionMap = new Map();
}
async assignServer(clientId) {
const serverId = process.env.SERVER_ID || `server-${Math.random().toString(36).slice(2)}`;
await this.redis.set(`session:${clientId}`, serverId, 'EX', 3600);
return serverId;
}
async getServer(clientId) {
return await this.redis.get(`session:${clientId}`);
}
async releaseSession(clientId) {
await this.redis.del(`session:${clientId}`);
}
}
// WebSocket server with session affinity
const wss = new WebSocket.Server({ port: 8080 });
const sessionManager = new StickySessionManager();
wss.on('connection', async (ws, req) => {
const clientId = req.headers['x-client-id'];
if (clientId) {
await sessionManager.assignServer(clientId);
console.log(`Client ${clientId} assigned to this server`);
}
ws.on('close', async () => {
if (clientId) {
await sessionManager.releaseSession(clientId);
}
});
});
// Load balancer routing logic
async function routeRequest(clientId) {
const serverId = await sessionManager.getServer(clientId);
if (serverId) {
return `http://${serverId}:8080`;
}
// Round-robin fallback
return selectNextServer();
}
Expected output: Application-level sticky session tracking using Redis, enabling custom routing logic in the load balancer.
Common Mistakes
| Mistake | Explanation |
|---|---|
| Relying on IP-based stickiness behind NAT | Multiple users behind the same NAT appear as one IP, causing incorrect routing |
| Using short timeouts for sticky cookies | WebSocket connections can last hours; set cookie TTL appropriately |
| Not configuring consistent hashing | Without consistent hashing, adding/removing servers redistributes all connections |
| Forgetting health checks | Unhealthy servers with active sessions cause connection drops; implement graceful failover |
| Using HTTP-only sticky methods for TCP mode | HTTP sticky methods require HTTP mode; use TCP mode for raw WebSocket proxying |
Practice Questions
- Why do WebSocket connections need sticky sessions?
- How does cookie-based stickiness differ from IP-based stickiness?
- What is consistent hashing and why is it useful for WebSocket?
- How do you implement sticky sessions with NGINX?
- What are alternatives to sticky sessions for scaling WebSocket?
Challenge
Set up a three-node WebSocket server cluster with NGINX sticky sessions. Implement a chat application that maintains per-user state on each server. Test that reconnecting clients are routed to the same server and maintain their state.
FAQ
Mini Project
Deploy a WebSocket chat application behind HAProxy with cookie-based sticky sessions. Set up three backend servers, implement a session state store in Redis, and create a dashboard showing which server each client is connected to.
What's Next
Learn about WebSocket load balancing strategies
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro