WebSocket Tester
Compose frames, script replies and step a WebSocket session on this device — with the handshake key computed and every close code decoded.
This is a simulator, not a client
Simulates WebSocket sessions on this device — it does not and cannot open a connection from a static page.
There is no server-address field on this page and no socket behind it: the timeline, the handshake value and every decoded code are computed from what you type. Open your network tab and watch — running a session sends nothing.
A message can be up to 16,384 characters and the script up to 65,536 — past either, the engine refuses and says which line.
A session stops at 500 timeline events — a longer script is refused rather than truncated.
- +0 msOpening handshake complete — the server answered 101 Switching Protocols and the connection is OPEN (RFC 6455 §4.2.2).
handshake · HTTP/1.1 101 Switching Protocols · connection OPEN - +0 msClient sends a text frame — every frame a client sends is masked with a fresh 32-bit key (RFC 6455 §5.1).
client → server · text frame (opcode 0x1, FIN=1, masked) · "Hello over the wire" - +0 msServer echoes the payload back, unmasked — servers never mask the frames they send (RFC 6455 §5.1).
server → client · text frame (opcode 0x1, FIN=1, unmasked) · "Hello over the wire" - +0 msClient sends a text frame — every frame a client sends is masked with a fresh 32-bit key (RFC 6455 §5.1).
client → server · text frame (opcode 0x1, FIN=1, masked) · "One more" - +0 msServer echoes the payload back, unmasked — servers never mask the frames they send (RFC 6455 §5.1).
server → client · text frame (opcode 0x1, FIN=1, unmasked) · "One more" - +0 msClient starts the closing handshake with code 1000 (Normal Closure) — after sending Close it may send no further data (RFC 6455 §5.5.1).
client → server · close frame (opcode 0x8, masked) · code 1000 "done" - +0 msServer responds with a Close frame echoing code 1000 — an endpoint that receives Close and has not sent one must respond (RFC 6455 §5.5.1).
server → client · close frame (opcode 0x8, unmasked) · code 1000 - +0 msClosing handshake complete — the connection is CLOSED and the close code your code reads is 1000 (Normal Closure) (RFC 6455 §7.1.1).
TCP connection closed · session ended with code 1000 (Normal Closure)
8 events · 2 sent · 2 received · 0 ping · 0 pong
1006 Abnormal Closure never on the wire
A reserved signal code: the connection dropped without any Close frame at all. Reported locally, never sent on the wire.
Network drops, crashed servers and idle proxies cutting the socket — the code browsers report for most failed connections.
- Code 1006 is a signal code the API reports — it is never carried in a Close frame on the wire (RFC 6455 §7.4.1).
Source: RFC 6455 §7.4.1
| Code | Name | Meaning | On the wire | Source |
|---|---|---|---|---|
| 1000 | Normal Closure | The purpose for which the connection was established has been fulfilled — a clean, intended shutdown. | sent in Close frames | RFC 6455 §7.4.1 |
| 1001 | Going Away | An endpoint is going away — a server shutting down, or a browser navigating away from the page. | sent in Close frames | RFC 6455 §7.4.1 |
| 1002 | Protocol Error | An endpoint is terminating the connection because the peer violated the protocol. | sent in Close frames | RFC 6455 §7.4.1 |
| 1003 | Unsupported Data | An endpoint received a type of data it cannot accept — for example, binary frames on a text-only service. | sent in Close frames | RFC 6455 §7.4.1 |
| 1004 | Reserved | Reserved — a specific meaning might be defined in the future. Endpoints must not use it. | never on the wire | RFC 6455 §7.4.1 |
| 1005 | No Status Rcvd | A reserved signal code: the connection closed with no status code in the Close frame. Reported by the API, never sent in one. | never on the wire | RFC 6455 §7.4.1 |
| 1006 | Abnormal Closure | A reserved signal code: the connection dropped without any Close frame at all. Reported locally, never sent on the wire. | never on the wire | RFC 6455 §7.4.1 |
| 1007 | Invalid frame payload data | A message's payload was inconsistent with its type — for example, malformed UTF-8 inside a text frame. | sent in Close frames | RFC 6455 §7.4.1 |
| 1008 | Policy Violation | An endpoint received a message that violates its policy — the generic code when nothing more specific fits or the detail must stay hidden. | sent in Close frames | RFC 6455 §7.4.1 |
| 1009 | Message Too Big | An endpoint received a message too big for it to process. | sent in Close frames | RFC 6455 §7.4.1 |
| 1010 | Mandatory Ext. | The client expected the server to negotiate one or more extensions, and the handshake did not return them. | sent in Close frames | RFC 6455 §7.4.1 |
| 1011 | Internal Error | The server is terminating the connection because it hit an unexpected condition that prevented it from fulfilling the request. | sent in Close frames | RFC 6455 §7.4.1 |
| 1012 | Service Restart | The service is restarting. A client may reconnect, ideally after a randomized delay. | sent in Close frames | IANA WebSocket Close Code Number Registry |
| 1013 | Try Again Later | The service is overloaded or otherwise cannot serve the connection right now — come back later. | sent in Close frames | IANA WebSocket Close Code Number Registry |
| 1014 | Bad Gateway | The server was acting as a gateway or proxy and received an invalid response from upstream — the analogue of HTTP's 502. | sent in Close frames | IANA WebSocket Close Code Number Registry |
| 1015 | TLS handshake | A reserved signal code: the connection never opened because the TLS handshake failed. Reported locally, never sent. | never on the wire | RFC 6455 §7.4.1 |
A share link carries the view and the loaded preset only — never your composed script or pasted key.
What this page computes
Each row is computed on this device and cites the clause it comes from.
- Sec-WebSocket-Accept computed from a pasted Sec-WebSocket-Key — SHA-1 over the key plus the RFC 6455 GUID, base64-encoded (§4.2.2)
- Every protocol close code, 1000–1015, with its meaning, wire behaviour and source — plus the 1016–2999, 3000–3999 and 4000–4999 band rules (§7.4)
- Frame header arithmetic — FIN, opcode, the mask bit, 7/16/64-bit payload lengths and the exact header size in bytes (§5.2)
- A deterministic scripted session — sends, scripted replies, echo, ping/pong and the closing handshake, stepped on a timeline (§5.5, §7.1)
What it does not do
- Opening a real ws:// or wss:// connection. Nothing here reaches the network — the session is simulated on this device
- Sec-WebSocket-Protocol negotiation between real endpoints
- permessage-deflate and other extension behaviour
- Binary payload editing — the composer speaks text frames only in this version
Press ? for shortcuts
Do it offline:$ printf '%s%s' 'dGhlIHNhbXBsZSBub25jZQ==' '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' | openssl dgst -sha1 -binary | openssl base64
How to use it
- Write the session in the composer, one directive per line: send for client frames, reply for scripted server frames, echo on to make the pretend server repeat everything back.
- Run plays the whole script onto the timeline; Step reveals one frame at a time in protocol order, client frames masked, server frames not.
- Finish with close and a status code to watch the closing handshake: the Close frame, the peer's echo of it, and the code an onclose handler would read.
- Type any code into the rail to decode it, or scan the 1000–1015 table — each row says whether it ever appears on the wire and names its source.
- Switch views for the other two jobs: Handshake key computes Sec-WebSocket-Accept from a pasted Sec-WebSocket-Key, and Frame anatomy prices a frame's header byte by byte.
A socket dies and the console offers close code 1006 and nothing else. Script both sides of the session instead: compose what the client sends, write the server's replies, and step the exchange on a timeline — with every close code decoded from the registry and the Sec-WebSocket-Accept value computed from a pasted key. Nothing connects here: a static page cannot open a socket, and this one never pretends to.
Examples
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=The spec's own vector: SHA-1 over the key with the fixed GUID appended, base64-encoded. The openssl line below produces the same answer from the same bytes.
close code 10061006 Abnormal Closure
A reserved signal code: the connection dropped without any Close frame at all. Reported locally, never sent on the wire.
Code 1006 is a signal code the API reports — it is never carried in a Close frame on the wire (RFC 6455 §7.4.1).Edge case, and the most-searched code of them all: no endpoint ever sends 1006. It is the browser's word for a session that ended without a closing handshake.
echo on · send Hello · close 1000 done+0 ms handshake · HTTP/1.1 101 Switching Protocols · connection OPEN
+0 ms client → server · text frame (opcode 0x1, FIN=1, masked) · "Hello"
+0 ms server → client · text frame (opcode 0x1, FIN=1, unmasked) · "Hello"
+0 ms client → server · close frame (opcode 0x8, masked) · code 1000 "done"
+0 ms server → client · close frame (opcode 0x8, unmasked) · code 1000
+0 ms TCP connection closed · session ended with code 1000 (Normal Closure)A three-line script: echo mode answers the send, then the closing handshake runs in both directions and the session ends with 1000.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
An https:// page cannot open ws:// — mixed content
Secure pages may only open wss:// sockets; plain ws:// is blocked as mixed content before any frame exists. That failure produces no close code at all, which is why the console so often has nothing to say about it.
Client frames are masked, server frames never are
Every frame a client sends is XORed with a fresh 32-bit key; a server that receives an unmasked client frame must fail the connection, and a client must do the same for a masked server frame. The simulator's wire lines carry the rule in every row.
1005 and 1006 exist only in your callback
Both are signal codes: 1005 means the Close frame carried no code, 1006 means there was no Close frame at all. Neither is ever written into a Close frame — which is why the composer refuses to send them.
Ping and pong keep idle sessions alive
A ping is a control frame of at most 125 payload bytes, and the answering pong must carry identical payload data. Idle connections without them are exactly the ones proxies cut — surfacing later as 1006.
The length field comes in three sizes, always minimal
Payloads to 125 bytes fit the 7-bit field; 126 borrows two more bytes; 127 borrows eight. The smallest encoding that fits is mandatory, and the Frame anatomy view shows the header growing at each boundary.
Common errors
Sec-WebSocket-Key is not valid base64 — its length (22) is not a multiple of 4.- Usually a truncated paste: a conforming nonce is 16 random bytes, so its base64 form is exactly 24 characters ending in ==.
Line 1: code 1006 (Abnormal Closure) cannot be sent in a Close frame — it is reserved as a signal the API reports (RFC 6455 §7.4.1).- The refusal is the lesson: no endpoint can send 1006. To study an abnormal drop, decode the code in the rail instead.
Close codes 0–999 are not used (RFC 6455 §7.4.2) — 999 cannot appear; the defined range starts at 1000.- A refusal from the decoder, not a verdict about your endpoint: nothing below 1000 is defined, so there is nothing to decode.
Line 1: `connect` is not a directive — use send, reply, ping, echo on|off, wait <ms>, close <code> [reason] or server-close <code> [reason].- There is deliberately no connect directive. The simulator scripts both endpoints on this device — reaching a real server is the one thing it will not pretend to do.
FAQ
Can this connect to my ws:// server?
No. This page opens no connection of any kind — there is no address field, and neither the engine nor the page has a network path. Every frame on the timeline is computed on this device from the script you wrote. To exercise a real endpoint, use a client on your own machine, such as wscat or websocat.
Why does the browser report close code 1006 so often?
Because 1006 stands for every close that never happened: a dropped connection, a proxy timeout, a crashed process — anything that ends a session without a Close frame. The browser can only report that it saw nothing; the actual reason lives in server and proxy logs.
Why must every client frame be masked?
It protects intermediaries, not secrecy: XORing the payload with a fresh 32-bit key stops a crafted message from looking like a plain HTTP request to a caching proxy along the way. The rule is one-directional by design — servers must never mask.
Keyboard shortcuts
- ⌘/Ctrl + Enter
- Run the script into the timeline.
- ⌘/Ctrl + C
- Copy the pane on screen when nothing is selected.
- ⌘/Ctrl + Shift + C
- Copy the pane on screen regardless of selection.
- ⌘/Ctrl + S
- Download the pane on screen as a text file.
- t
- Cycle the colour theme.
- ? or ⌘/Ctrl + /
- Open the shortcut list.
- Esc
- Close the dialog on top.