WebSocket is a protocol that, since 2011, enables real‑time bidirectional communication between a browser and a server. Unlike traditional HTTP, which operates in a request‑response model, WebSocket maintains a persistent connection, eliminating the need to constantly establish new connections. This allows applications such as chat services, online games, or real‑time monitoring dashboards to run smoothly with minimal network overhead.
How does WebSocket work in the browser?
A WebSocket connection starts with a classic HTTP Upgrade request. The browser sends the header Upgrade: websocket and Sec-WebSocket-Key. If the server supports the protocol, it responds with status 101 Switching Protocols and returns Sec-WebSocket-Accept, which is the SHA‑1 and Base64 result of the client’s key. After this “handshake” completes, both sides switch to binary mode, and subsequent frames are transmitted without HTTP headers.
In the browser, the WebSocket interface provides simple methods: new WebSocket(url), send() and events onmessage, onopen, onclose, and onerror. This lets developers focus on application logic without worrying about low‑level protocol details.
Advantages of WebSocket vs HTTP polling
- A persistent connection eliminates the cost of opening and closing TCP/IP for each request.
- Frames have only a few bytes of header, significantly reducing overhead compared to a full HTTP header on every refresh.
- Bidirectional communication – the server can initiate messages, which is impossible with plain HTTP without techniques like long‑polling.
In practice this translates to lower bandwidth consumption, reduced latency, and better scalability with a large number of concurrent clients.
WebSocket implementation in Node.js
In the Node.js ecosystem the most popular library is ws. Install it with npm install ws, then create a server:
const http = require('http');
const WebSocket = require('ws');
const server = http.createServer();
const wss = new WebSocket.Server({ server });
wss.on('connection', ws => {
console.log('New connection');
ws.on('message', msg => {
console.log('Received:', msg);
// Echo to all clients
wss.clients.forEach(client => {
if (client.readyState === WebSocket.OPEN) {
client.send(`Echo: ${msg}`);
}
});
});
});
server.listen(8080, () => console.log('Server listening on 8080'));
In this example the HTTP and WebSocket servers share the same port, simplifying configuration in cloud environments. It’s advisable to add ping/pong handling to detect disconnected clients and to limit the maximum message size, which improves stability.
WebSocket and scalability in Docker and Kubernetes
Containerization does not change the protocol itself, but it introduces challenges related to connection distribution. Because WebSocket maintains a persistent connection, traditional round‑robin load balancers may route traffic randomly, leading to a situation where a client’s connection ends up on a different pod after a short interruption. Therefore, in Kubernetes it is recommended to use session affinity or a proxy layer such as nginx in stream mode, which maintains “sticky sessions”.
Sample nginx configuration:
stream {
upstream ws_backend {
server ws-app-1:8080;
server ws-app-2:8080;
sticky;
}
server {
listen 443 ssl;
proxy_pass ws_backend;
proxy_ssl_certificate /etc/ssl/cert.pem;
proxy_ssl_certificate_key /etc/ssl/key.pem;
}
}
Additionally, to enable horizontal scaling, it’s useful to separate message distribution logic from the WebSocket server itself. A common solution is to use a message‑queue system (e.g., Redis Pub/Sub, NATS) – each pod publishes events, and all pods subscribe and forward them to connected clients.
Security of WebSocket connections
The foundation is using the wss:// protocol, i.e., WebSocket over TLS. A TLS certificate encrypts the entire channel, just like HTTPS. Moreover, it’s advisable to implement authorization during the handshake – typically via a JWT token sent in the Sec-WebSocket-Protocol header or as a query parameter. The server validates the token and rejects unauthorized connections.
After the connection is established, it’s recommended to limit permissions at the application level: for example, assigning rooms only to specific users. Monitoring message rates (rate limiting) and employing protection mechanisms against DoS attacks, which can flood the server with hundreds of thousands of open connections, is also important.
“A stable and secure WebSocket is not just a protocol – it’s a set of practices that must accompany every production implementation.”
Practical WebSocket implementation checklist
- Use
wss://and install a TLS certificate. - Require authentication during the handshake (JWT, OAuth).
- Configure sticky sessions in the load balancer or proxy.
- Implement a ping/pong mechanism and timeouts.
- Separate message broadcasting logic (e.g., Redis Pub/Sub).
- Limit message size and frequency (rate limiting).
Common mistakes and trade‑offs
One of the most frequent errors is skipping the proxy layer when deploying to Kubernetes, which leads to connection loss after a short period. Another issue is the lack of encryption – WebSocket connections without TLS are vulnerable to man‑in‑the‑middle attacks. Developers often also assume that WebSocket solves all performance problems; in reality, with a very high number of concurrent connections, an additional queuing mechanism and horizontal scaling are required.
Trade‑offs appear when choosing message size. Too small frames increase the number of I/O operations, while too large ones can cause fragmentation and higher memory consumption. The optimal approach is to profile the application for typical payload size and adjust limits accordingly.
In summary, the WebSocket protocol’s operation and usage is a powerful tool for real‑time applications, but it requires a conscious approach to scalability and security. If you need help designing a WebSocket‑based architecture, integrating with Kubernetes, or conducting a security audit, feel free to collaborate with Coderia.it – together we’ll build solid and high‑performance solutions.



