In Part 1 the server learned to talk without being asked — but the browser could still only listen. A chat, a multiplayer game, a collaborative editor: these need both sides speaking freely over one connection. This is Part 2 of the series, and it's about the browser's true two-way channels — WebSockets, the workhorse, and WebTransport, its modern successor.
Beyond HTTP is a three-part series. Part 1 — server push. Part 2 — two-way channels (you are here). Part 3 — browser to browser: WebRTC.
The mental model: one open line, both sides talk
A two-way channel is a phone call that neither side hangs up — either party can speak the instant it has something to say. Server-Sent Events from Part 1 kept the line open but muted the browser; here the browser gets its voice back. Once the channel is established, there is no request and no response, just messages flowing in both directions whenever either end sends one. That property has a name: full-duplex, both directions at once.
Everything in this post is a variation on that idea. The two technologies differ in what they run on top of — and that single choice of transport explains almost all of their differences.
WebSockets: an HTTP handshake, then a raw channel
A WebSocket starts life as an ordinary HTTP request and then asks to be upgraded into a persistent two-way connection. This is the clever part of its design: it borrows HTTP just long enough to get through firewalls and reuse the same port, then leaves HTTP behind entirely.
The browser sends a normal-looking request with a special header asking to switch protocols:
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
If the server agrees, it answers not with 200 OK but with 101 Switching Protocols. From that moment the connection is no longer HTTP — it is a bare, full-duplex pipe over Transmission Control Protocol (TCP), the reliable ordered-delivery protocol most of the web runs on.
On the client the API is small. You open a socket, and from then on send() and an onmessage handler are all you need — in either direction, at any time:
const socket = new WebSocket('wss://example.com/chat');
socket.onopen = () => socket.send('hello');
socket.onmessage = (event) => render(event.data);
Note the wss:// scheme — the encrypted form, WebSocket over Transport Layer Security (TLS), the secure default just as https:// is for pages. WebSocket messages are framed, so unlike a raw stream you receive whole messages, not arbitrary byte chunks, and they can carry text or binary. An optional subprotocol (negotiated in the handshake) lets both ends agree on a message format, like a mini application protocol of your own.
The WebSocket gotchas
WebSockets give you a channel and almost nothing else — everything above "bytes in, bytes out" is your problem. That minimalism is why they're everywhere, but it's worth knowing what you're signing up for:
- No automatic reconnection. Unlike Server-Sent Events, a dropped WebSocket stays dropped. You write the reconnect-and-resume logic yourself.
- No built-in message types or routing. A WebSocket is one undifferentiated stream of frames. Channels, request/response correlation, acknowledgements — you invent them (this is what subprotocols and libraries like Socket.IO add on top).
- Head-of-line blocking. Because it rides on a single TCP connection, one lost packet stalls every message behind it until the network resends it — even unrelated ones. TCP guarantees order, and that guarantee becomes a bottleneck under packet loss.
- Scaling needs stickiness. A WebSocket is a long-lived connection pinned to one server process. Spread users across many servers and you need sticky routing plus a way to broadcast between servers (a message bus), because the user on server A isn't connected to server B.
None of these are dealbreakers — WebSockets have powered real-time apps for over a decade. But the last two, especially head-of-line blocking, are exactly what the next technology set out to fix.
WebTransport: two-way over HTTP/3
WebTransport is a newer two-way API built on HTTP/3 and QUIC, designed to fix WebSocket's transport-level weaknesses. QUIC is a modern transport protocol that runs over the User Datagram Protocol (UDP) instead of TCP, and it changes the game in two ways that matter here.
First, independent streams. A single WebTransport connection can carry many concurrent streams, and because QUIC tracks them separately, a lost packet in one stream doesn't stall the others — the head-of-line blocking that plagues WebSockets is gone. Second, unreliable datagrams. Alongside reliable ordered streams, WebTransport offers fire-and-forget datagrams that are not retransmitted if lost — perfect for data where the newest value matters more than every value (a player's position, a live cursor), where a late-arriving old packet is worthless anyway.
const transport = new WebTransport('https://example.com:4433/wt');
await transport.ready;
// Reliable, ordered — like a WebSocket message:
const stream = await transport.createBidirectionalStream();
// Unreliable, fire-and-forget — the newest value wins:
const writer = transport.datagrams.writable.getWriter();
writer.write(positionUpdate);
The trade-off used to be maturity, and this is the fact worth updating in your head: as of early 2026 WebTransport is supported across all major browsers, so its "too new to use" era is largely over. WebSockets still win on ubiquity of tooling, server support, and battle-tested libraries, but WebTransport is no longer an experiment — it's the better default for anything latency-sensitive that suffers under head-of-line blocking.
WebSocket vs WebTransport (and where SSE fits)
Choose by how much control you need over reliability and how much you value maturity over transport efficiency. Here is the whole picture, including Part 1's Server-Sent Events for contrast:
| Feature | Server-Sent Events | WebSocket | WebTransport |
|---|---|---|---|
| Direction | Server → browser | Full-duplex | Full-duplex |
| Underlying transport | HTTP | TCP | HTTP/3 (QUIC / UDP) |
| Message types | Text only | Text + binary | Binary streams + datagrams |
| Auto-reconnect | |||
| Head-of-line blocking | |||
| Unreliable mode | |||
| Tooling & maturity |
— green is the stronger choice for that row; red is the weaker one.
In practice: reach for WebSockets when you want a proven, well-supported two-way channel and simple ordered messaging is enough — most chats and dashboards. Reach for WebTransport when the workload is latency-critical and hurt by head-of-line blocking, or when you genuinely want unreliable datagrams — real-time games, live media control, high-frequency telemetry.
Where this leaves us
WebSockets and WebTransport both give the browser a full, equal voice — but always in a conversation with a server. The connection runs client to server, and if two users want to talk, their bytes still pass through the middle. The last frontier is cutting the server out of the data path entirely and letting one browser talk straight to another — which is a different problem, and a different technology. That is Part 3: WebRTC.
Glossary
| Term | What it is |
|---|---|
| Full-duplex | Both ends can send at the same time over one connection. |
| WebSocket | A two-way channel that begins as an HTTP request upgraded to a raw TCP connection. |
| TCP | Transmission Control Protocol — reliable, ordered byte delivery; the basis of most of the web. |
101 Switching Protocols | The HTTP status a server returns to accept a WebSocket upgrade. |
| Head-of-line blocking | One lost packet stalling all messages queued behind it on the same connection. |
| WebTransport | A two-way browser API over HTTP/3, offering independent streams and unreliable datagrams. |
| QUIC | A modern transport protocol over UDP that underpins HTTP/3; streams don't block each other. |
| UDP | User Datagram Protocol — fast, connectionless delivery with no ordering or retransmission. |
| Datagram | A single fire-and-forget packet that is not retransmitted if lost. |
References
- WebSockets API — MDN — the client API, framing, and the
wss://scheme. - RFC 6455 — The WebSocket Protocol — the normative spec, including the
Upgradehandshake and101response. - WebTransport API — MDN — streams, datagrams, and the connection lifecycle.
- WebTransport is now Baseline — WebRTC.ventures — on WebTransport reaching cross-browser support in 2026.
- caniuse: WebTransport — current browser support at a glance.
