HTTP is a request-response protocol: the client asks, the server answers, the connection closes. That works perfectly for loading web pages and APIs, but it's terrible for real-time features like chat, live notifications, collaborative editing, stock tickers, or multiplayer games. WebSockets solve this by opening a persistent, bidirectional connection between client and server.

How WebSockets Work

A WebSocket connection starts as an ordinary HTTP request — the "handshake." The client sends an Upgrade: websocket header, and if the server supports it, it responds with a 101 Switching Protocols status. From that point on, both sides can send messages to each other at any time, without the overhead of new HTTP requests.

WebSocket Handshake
Client → Server:
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

Server → Client:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

HTTP vs WebSockets

  • HTTP: Client-initiated, request-response, stateless, high overhead per message (headers resent each time)
  • WebSocket: Bidirectional, persistent, stateful, minimal per-message overhead (2-14 bytes framing)
  • SSE (Server-Sent Events): Server-to-client only, built on HTTP, simpler than WebSockets for one-way data streams

Browser Implementation

Client-Side JavaScript
const ws = new WebSocket("wss://api.example.com/ws");

// Connection opened
ws.addEventListener("open", () => {
  console.log("Connected!");
  ws.send(JSON.stringify({ type: "join", room: "general" }));
});

// Listen for messages
ws.addEventListener("message", (event) => {
  const data = JSON.parse(event.data);
  console.log("Received:", data);
});

// Connection closed
ws.addEventListener("close", (event) => {
  console.log(`Disconnected: ${event.code} ${event.reason}`);
});

// Handle errors
ws.addEventListener("error", (error) => {
  console.error("WebSocket error:", error);
});

Node.js Server with ws

Server-Side (Node.js)
import { WebSocketServer } from "ws";

const wss = new WebSocketServer({ port: 8080 });

wss.on("connection", (ws) => {
  console.log("Client connected");

  ws.on("message", (message) => {
    const data = JSON.parse(message);

    // Broadcast to all connected clients
    wss.clients.forEach((client) => {
      if (client.readyState === 1) {
        client.send(JSON.stringify({
          type: "message",
          text: data.text,
          timestamp: Date.now()
        }));
      }
    });
  });

  ws.on("close", () => {
    console.log("Client disconnected");
  });
});

When to Use WebSockets

  • ✅ Use WebSockets for: Chat apps, live notifications, collaborative editing (Google Docs-style), multiplayer games, live sports scores, stock tickers, IoT dashboards
  • ❌ Don't use WebSockets for: Standard CRUD APIs, file uploads, infrequent data polling (use SSE or long-polling instead), simple notification badges

Production Considerations

  • Reconnection logic: Connections drop. Implement exponential backoff reconnection on the client.
  • Heartbeats (ping/pong): Send periodic pings to detect dead connections. Stale connections waste server memory.
  • Authentication: Pass a JWT token in the connection URL or first message. Don't rely on cookies alone.
  • Scaling: WebSocket connections are stateful. Use a pub/sub system (Redis, NATS) to share messages across multiple server instances.
  • Load balancers: Not all load balancers handle WebSocket upgrades correctly. Ensure your LB supports sticky sessions or WebSocket protocol.

Try Our Free Developer Tools

Encode, decode, and format your code instantly.