This episode dissects how a WebSocket connection is born and dies: handshake headers, the Sec-WebSocket-Accept calculation, the four connection states, frame structure, as well as the close frame and heartbeat mechanisms.

Every WebSocket connection begins with something ordinary: an HTTP request. Episode 3 dissects those early moments — the opening handshake — then follows the connection's journey through to its closure.
You will understand why the handshake is required, how the server proves it understands the WebSocket protocol, the four states a connection passes through, the anatomy of the frames that carry data, and the ping and pong mechanisms that keep the connection alive. These are the details that make your WebSocket application behave correctly in production.
When a browser or client opens a WebSocket connection, the first thing sent is an HTTP request with special headers. Look at these headers:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13The Upgrade: websocket header tells the server that the client wants to switch protocols, and Sec-WebSocket-Key is a random nonce the server will use to prove its understanding of RFC 6455.
If the server accepts, it returns:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=Status 101 Switching Protocols signals that the connection is switching from HTTP to WebSocket. The key to this process lies in the Sec-WebSocket-Accept header: its value is not arbitrary, but the result of a calculation performed on the Sec-WebSocket-Key.
The server combines the received key with a standard GUID, then hashes it:
const crypto = require("crypto");
function buatAccept(key) {
const GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
const hash = crypto.createHash("sha1").update(key + GUID).digest("base64");
return hash;
}
const accept = buatAccept("dGhlIHNhbXBsZSBub25jZQ==");
console.log(accept);The crypto.createHash("sha1") formula proves that the server truly understands the protocol. If the client does not receive the matching Sec-WebSocket-Accept, the connection is considered failed.
Two optional headers can also take part in the handshake:
permessage-deflate for message compression.WebSocket defines four connection states. In the browser, you read them through the readyState property:
const ws = new WebSocket("ws://localhost:8080");
console.log(ws.readyState); // 0 = CONNECTING
ws.onopen = () => {
console.log(ws.readyState); // 1 = OPEN
};new WebSocket("ws://localhost:8080") automatically opens the handshake right away. Notice the state transitions: from 0 to 1 after onopen, and toward 2 then 3 when the closure begins.
After the handshake, all data is sent in frames. Each frame begins with a small header containing:
An important rule in RFC 6455: all client-to-server frames must be masked, while server-to-client frames are not. Masking prevents cache poisoning on legacy proxies that misinterpret frames.
A single WebSocket connection goes through these phases:
Closure is not a casual disconnect. One side sends a close frame with a code and reason:
ws.close(1000, "selesai");Code 1000 means a normal closure. Other common codes: 1001 going away, 1006 abnormal connection without a close frame, and 1009 message too big. Using ws.close(1000, "selesai") with a clear reason helps debugging in production.
Because a connection can stay idle for too long, the server routinely sends ping and the client must reply with pong. This is early detection of dead connections that in a later episode we will build into a complete heartbeat system with timeouts.
Tip
Do not rely on TCP timeouts alone. Many proxies and load balancers drop idle connections after a few minutes. Regular ping/pong keeps the connection looking active and prevents silent disconnects.
Episode 3 dissected the mechanics behind every WebSocket connection: the upgraded HTTP handshake, the Sec-WebSocket-Accept calculation, the four connection states, the frame structure with masking rules, the close frame, and the ping/pong heartbeat.
Key takeaways:
In the next episode we will come up to the surface: the native WebSocket API in the browser — creating a WebSocket instance, the four event handlers, the send and close methods, and the readyState and bufferedAmount properties. You will start writing real client code.