WebSocket

On this page 7

WebSocket is a communication protocol, standardized as RFC 6455, that turns a single HTTP request into a persistent, full-duplex connection over which a browser and a server can send messages to each other at any time. Among the Web APIs, it is exposed to scripts through the WebSocket interface, which lets a page open a connection, send text or binary data and react to incoming messages through events.

The protocol exists because classic HTTP is request-response: the client asks, the server answers, and the server cannot speak first. WebSocket removes that constraint after a brief opening handshake, which makes it the standard choice for chat, live dashboards, multiplayer games, collaborative editing and trading screens where low latency in both directions matters more than cacheability.

How the WebSocket handshake and framing work

A WebSocket connection starts as an ordinary HTTP/1.1 GET request carrying the headers Upgrade: websocket, Connection: Upgrade, a random Sec-WebSocket-Key and Sec-WebSocket-Version: 13. A server that accepts replies with status 101 Switching Protocols and a Sec-WebSocket-Accept value derived from the key, which proves it understood the request. From that moment the same TCP connection carries WebSocket frames instead of HTTP messages.

Data travels in frames with a compact header of 2 to 14 bytes, far smaller than the headers of a typical HTTP request. Each frame carries an opcode that identifies its type, a payload length and, for frames sent by the client, a 32-bit masking key. Masking does not provide confidentiality; it exists to stop malicious scripts from poisoning intermediary caches with crafted byte sequences. Large messages can be split across several frames and reassembled by the receiver.

The following table lists the frame types defined by the protocol and what each one does.

OpcodeFrame typePurpose
0x0ContinuationCarries the next fragment of a split message
0x1TextUTF-8 encoded message, typically JSON
0x2BinaryRaw bytes, such as Protocol Buffers or media
0x8CloseStarts the closing handshake with a status code
0x9PingChecks that the peer is still alive
0xAPongAnswers a ping or acts as a one-way heartbeat
WebSocket frame opcodes

Connections use the ws:// scheme on port 80 or the encrypted `wss://` scheme on port 443, where the frames run inside TLS exactly as HTTPS traffic does. In practice `wss://` is the only sensible choice on the public internet, because proxies and middleboxes often break or block unencrypted upgrades.

The browser WebSocket API

In the browser, a script creates a connection with new WebSocket(url, protocols) and works with it through four events and two methods. The optional protocols argument negotiates an application subprotocol through the Sec-WebSocket-Protocol header, so both sides agree on the message format, for example STOMP or a custom JSON schema.

The table below summarizes the members a frontend developer uses most often.

MemberKindWhat it does
openEventFires when the handshake succeeds
messageEventDelivers each received message in event.data
errorEventSignals a failure, always followed by close
closeEventReports the close code, reason and wasClean flag
send()MethodQueues text, a Blob or an ArrayBuffer for sending
close()MethodStarts a clean shutdown with an optional code
readyStatePropertyReports CONNECTING, OPEN, CLOSING or CLOSED
bufferedAmountPropertyCounts bytes queued but not yet transmitted
Core members of the WebSocket interface

The classic API has no built-in backpressure: send() never blocks, so a fast producer can fill memory while bufferedAmount grows. Careful code checks that property before sending more. The newer, promise-based WebSocketStream proposal addresses this with streams, but its browser support remains limited.

WebSocket compared with other real-time techniques

WebSocket is one of several ways to push data to a page, and it is not always the best one. Its neighbors in the Web APIs topic, especially Server-Sent Events and WebRTC, solve related problems with different trade-offs.

TechniqueDirectionTransportBest fit
WebSocketBoth waysUpgraded TCP connectionChat, games, collaborative editing
Server-Sent EventsServer to clientPlain HTTP streamNotifications, feeds, live scores
Long pollingServer to client, emulatedRepeated HTTP requestsLegacy fallback behind strict proxies
WebRTC data channelPeer to peerSCTP over DTLS and UDPLow-latency peer traffic, media
WebTransportBoth ways, multiple streamsHTTP/3 over QUICUnordered or unreliable delivery needs
Real-time options in the browser

Server-Sent Events are simpler when updates flow only from the server, because they reuse standard HTTP, reconnect automatically and multiplex well over HTTP/2. WebSocket wins when the client also sends frequent messages and when binary payloads are involved. A connection runs over TCP, so it inherits head-of-line blocking: one lost packet delays every message behind it, which is why fast-paced games sometimes prefer WebRTC or WebTransport.

Running WebSocket in production

A WebSocket server holds one long-lived connection per client, so capacity planning is about concurrent connections and memory rather than requests per second. A well-tuned event-driven server can keep tens of thousands of idle connections per instance, but the real limit depends on message rates, per-connection state and file-descriptor limits.

  • Load balancers and proxies must support the upgrade and use idle timeouts longer than the heartbeat interval, often 30 to 60 seconds.
  • Horizontal scaling needs a shared channel, such as a publish-subscribe broker, so a message reaches clients connected to other instances.
  • Reconnection with exponential backoff and jitter prevents a thundering herd when a server restarts and thousands of clients return at once.
  • Message resumption with sequence numbers lets a client request what it missed during a short disconnect.
  • Compression through the permessage-deflate extension saves bandwidth but costs CPU and memory per connection.

Security risks and common mistakes

Browsers do not apply the same-origin policy to WebSocket connections the way they do to fetch, and they send cookies with the handshake. A server that does not validate the `Origin` header is therefore exposed to cross-site WebSocket hijacking, in which a malicious page opens an authenticated connection on behalf of the victim.

The table pairs frequent problems with their usual cause and fix.

SymptomLikely causeFix
Connections drop after about 60 secondsProxy idle timeoutSend pings and raise proxy timeouts
Messages lost after reconnectNo resumption logicAdd sequence numbers and replay
Server memory keeps growingSlow clients, no backpressureCap buffers and disconnect laggards
Foreign sites act as the userMissing Origin checkAllowlist origins and use tokens
Handshake fails on some networksUnencrypted ws:// blockedUse wss:// on port 443
Typical WebSocket problems

Authentication deserves particular care. The browser API cannot set custom headers on the handshake, so teams pass a short-lived token in the first message or in a cookie rather than placing long-lived credentials in the URL, where logs may record them. Every incoming message must also be validated like any other untrusted input.

The short version

A normal web page talks to a server like someone sending letters: it asks a question, waits for the reply, then asks again. The server can never write first.

WebSocket turns that exchange into a phone call that stays open. After a quick greeting, both sides can talk whenever they have something to say, without dialing again each time. That is why chat messages, live scores and shared documents can update instantly without the page constantly asking whether anything has changed.

Where it makes a difference

A logistics company runs a dispatch dashboard showing the positions of about 2,000 delivery vans. The first version refreshed the map by polling a REST endpoint every five seconds, which produced roughly 400 requests per second at peak, mostly returning unchanged data, while positions still appeared several seconds late.

The team replaced polling with a wss:// connection per dispatcher. The server now pushes only changed positions as small binary frames, sends a ping every 25 seconds to keep proxies from closing idle connections, and tags each update with a sequence number so a browser that reconnects can request what it missed.

The result was a sharp drop in request volume and updates that reach the screen in well under a second. Dispatchers can also send reassignment commands over the same connection, so a change made by one dispatcher appears on every other screen almost immediately.