Learning objectives
After reading this article you will be able to:
- Understand how the handshake turns an HTTP request into a WebSocket connection
- Write a working WebSocket server and client in Node.js and the browser
- Decide when to use WebSocket and when HTTP or Server-Sent Events fit better
In plain HTTP, the client asks and the server answers. If the server has news, it cannot announce it: it has to wait for the next request. That works for a product page, but it is a poor fit for a chat, a live scoreboard or a metrics dashboard.
A WebSocket solves this with a connection that does not close after the response. While it is open, either side can send a message at any moment, with no new request. The protocol is defined in RFC 6455 and the browser API is documented on MDN.
How does a WebSocket start?#
With an ordinary HTTP request. The client asks to switch protocols, and the server agrees:
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=The 101 Switching Protocols status marks the switch. From then on the same TCP connection stops carrying HTTP and carries WebSocket messages instead.
The Sec-WebSocket-Accept header proves the server understands the protocol. It is the SHA-1 hash of the Sec-WebSocket-Key value plus a fixed GUID from the specification, encoded in Base64. This way the client does not mistake an ordinary HTTP server for a WebSocket server. You do not compute it by hand: the browser and the libraries do it for you.
Because it starts as HTTP, a WebSocket uses the same ports: 80 for ws:// and 443 for wss://.
What travels after the handshake?#
Frames. Each message goes in one or more frames, and the first byte says the type:
| Type | What it is for |
|---|---|
| Text | A UTF-8 message, usually JSON |
| Binary | Raw bytes: audio, images, formats such as Protobuf |
| Ping and pong | Checking that the other side is still alive |
| Close | Ending the connection with a code and a reason |
Frames from client to server are masked with a 4-byte key, and a server must disconnect a client that sends an unmasked frame. Masking is not encryption. To encrypt for real, use wss://.
A ping can come from either side, and the receiver answers with a pong carrying the same payload. Control frames carry at most 125 bytes.
How does a connection close?#
One side sends a Close frame with a code. The ones you will see most:
1000: normal closure.1001: the closing side is going away (a server going down or a browser leaving the page).1006: abnormal closure. The connection dropped with no Close frame, for example from a network failure or a proxy.1008: policy violation.1009: message too big.
How do you use a WebSocket in code?#
This server uses the ws library and forwards each message it receives to the other connected clients. Install it with npm install ws and set "type": "module" in package.json:
import { WebSocketServer, WebSocket } from "ws";
const wss = new WebSocketServer({ port: Number(process.env.PORT) || 8080 });
wss.on("connection", (socket) => {
socket.send("connected");
socket.on("message", (data, isBinary) => {
for (const client of wss.clients) {
if (client !== socket && client.readyState === WebSocket.OPEN) {
client.send(data, { binary: isBinary });
}
}
});
socket.on("close", (code) => console.log("closed", code));
});In the browser the API is built in, with nothing to install:
const socket = new WebSocket("ws://localhost:8080");
socket.addEventListener("open", () => socket.send("hi"));
socket.addEventListener("message", (event) => console.log(event.data));
socket.addEventListener("close", (event) => console.log("closed", event.code));Node.js also has a global WebSocket client (no longer marked experimental since version 22.4), so the same code works in scripts and tests. Use ws:// for local development and wss:// in production.
The socket.readyState property reports the state: 0 connecting (CONNECTING), 1 open (OPEN), 2 closing (CLOSING) and 3 closed (CLOSED). Check WebSocket.OPEN before sending, as in the example above.
When should you use a WebSocket?#
When messages flow in both directions, in real time and often:
- Chat and instant notifications.
- Games and shared cursors in collaborative editing.
- Live dashboards, such as prices and metrics.
For an API where the client asks and the server answers, a REST API is still the simplest choice, easy to cache and to test.
When should you not use a WebSocket?#
If only the server needs to push data, Server-Sent Events are enough. They are plain HTTP with Content-Type: text/event-stream, and the browser reconnects on its own. If the client only asks once in a while, polling every few seconds is simpler.
A WebSocket also has costs:
- State on the server. Each client holds an open connection and some memory. A thousand users means a thousand connections.
- No automatic reconnection. You write the retry logic, ideally with a growing delay.
- No custom headers in the browser. The
new WebSocket()constructor does not accept your own headers, so authentication usually goes through a cookie, a token in the URL (which can leak into logs) or a first authentication message. - No flow control. According to MDN, the
WebSocketAPI has no backpressure: if messages arrive faster than the page processes them, memory fills up. TheWebSocketStreamalternative fixes this, but it is non-standard and has limited support. MDN also points to WebTransport as the expected replacement for many uses, with narrower browser support. - Proxies cut idle connections. A periodic ping keeps traffic flowing.
How do you check who connects?#
Browsers always send an Origin header. Check that value on the server so another website cannot open connections on behalf of your user (Cross-Site WebSocket Hijacking). Clients that are not browsers can fake Origin, so it does not replace authentication.
Which terms are related to WebSocket?#
- TCP socket: the low-level connection. A WebSocket runs on top of one and adds the handshake, frames and messages.
- Webhook: an HTTP call a service makes when something happens. It does not keep a connection open.
- Subprotocol: an agreed format inside the WebSocket, negotiated in the handshake through the second argument of
new WebSocket(url, protocols).
To put a server like this online, read how to host a WebSocket server. If it sits behind a reverse proxy, the proxy must forward the Upgrade header. Behind a load balancer, each connection is pinned to one instance. And the TLS certificate is what makes wss:// possible.
How to do this on Square Cloud#
A WebSocket needs an always-on process, and that is how applications run on Square Cloud: in isolated containers, with no cold start and no sleep mode. With automatic restart on (the default for dashboard deploys), the app comes back by itself when the process exits with an error.
The platform network supports WebSockets, along with HTTP/3, a WAF and DDoS mitigation, and the proxy does not cut long connections. Every web application gets a *.squareweb.app subdomain with HTTPS, so your client can open wss:// on it directly. If you prefer a custom domain, it is available from the Standard plan.
You deploy the Node.js server from the example above, set the application's RAM in its settings and follow the logs in real time. The API hosting page has the details.
API hosting
APIs in always-on containers, with no cold starts
FAQs
Is a WebSocket the same as HTTP?
No. A WebSocket starts with an HTTP request, but after the handshake the connection stops being HTTP and exchanges WebSocket messages in both directions.
Which port does a WebSocket use?
By default port 80 for ws:// and port 443 for wss://, the same as HTTP and HTTPS. That is why it usually passes through firewalls and can share the port of your web server.
What is the difference between WebSocket and Server-Sent Events?
Server-Sent Events only send data from server to client. A WebSocket sends in both directions, as text or binary, but you must handle reconnection and heartbeats yourself.
Should I use ws:// or wss://?
In production, use wss://, which is WebSocket over TLS. Pages loaded over HTTPS are generally blocked from opening unencrypted ws://, and open traffic can be read along the way.
Does a WebSocket reconnect by itself when the connection drops?
No. When the connection closes, the close event fires and nothing else happens. The client code must reconnect, ideally with a growing delay between attempts.
More in APIs
See all 15 articles- How to host an APIHosting an API means running its HTTP server around the clock at a public address with HTTPS. Step-by-step with Node.js and Python, testing and common errors.
- How to host a Node.js appTo host a Node.js app, prepare package.json, read the port and secrets from the environment, pick an LTS version and run it with node. Step-by-step guide.
- How to host a FastAPI, Flask or Django appTo host a FastAPI, Flask or Django app, run it with a production server (Uvicorn or Gunicorn) on 0.0.0.0. Commands, Django settings and common errors included.
- How to host a Python app or scriptTo host a Python app or script, pin the version, isolate dependencies in a venv, read secrets from the environment and handle SIGTERM. Step-by-step guide.