Toolvore

WebSocket Tester

Connect to a WebSocket server, send messages, and log the traffic.

The connection goes straight from your browser to the endpoint you type in, not through us — so we never see the traffic, but every message you send does reach that server. Treat it exactly as you would any other client you point at a live system.

A ws test client: it opens a live WebSocket connection to any ws:// or wss:// endpoint, sends the messages you type, and keeps a timestamped log of everything travelling in both directions.

How to use it

  1. 1Enter the endpoint URL and press Connect; the pill beside it moves through connecting, open and closed.
  2. 2Type a message and press Send — Send stays disabled until the connection is open.
  3. 3Read the log, newest entry at the top, each stamped with your local clock time.

Example

Input
wss://echo.websocket.org, message Hello!
Output
a log with RECEIVED Hello! sitting above SENT Hello!, both above the INFO line recording that the connection opened

When the socket closes, the log records the numeric close code the server sent, which is usually more informative than the error line.

What happens to your data

The socket is a WebSocket object constructed in your own tab, so the traffic goes directly from your browser to the endpoint and no part of it passes through us — the flip side being that the endpoint sees your IP address and your own network's rules decide whether the handshake succeeds. The transcript is React state capped at 200 entries with the oldest discarded, and an unmount cleanup closes the socket, so leaving the page ends the session and takes the log with it.

Last updated August 2026

Your app shows nothing on screen and you cannot tell whether the server never sent the frame or your own onmessage handler quietly threw. From inside your code those two failures look identical, which is why the first move is a client that is not yours: something that opens the socket, sends one string, and prints whatever comes back.

Two things are worth settling before you type an address. The first is the scheme. A page served over https can only open a wss:// socket — a plaintext ws:// address is blocked as mixed content before any packet leaves. The second is authentication. The browser WebSocket API gives you a URL and nothing else, so an Authorization header is impossible from any page; the credential has to travel in the query string, in a cookie, in the subprotocol, or in the first message you send once the socket is open.

The common mistake is reading a failed connection as proof the server is down. Handshake failures are deliberately opaque to scripts — the browser will not tell JavaScript why an upgrade was rejected, because that would turn every page into a port scanner. The error line here is vague on purpose and points you at the browser console, and the informative part is the numeric close code that follows it.

Worth knowing what is out of reach too: no subprotocol field, no header editor, no automatic reconnect, no heartbeat. It is the plainest client there is, which is what you want when the question is whose fault this is.

How it works

Toolvore checks the address against one pattern — it must begin with ws:// or wss://, and anything else is refused with a message rather than attempted — then constructs a WebSocket object with the trimmed string and attaches four handlers. Everything on screen is those handlers writing lines: open and close write status lines, incoming frames write their payload, and your own sends are echoed into the same column. The weaknesses follow from how thin that is. Only text goes out, because the message box produces a string: there is no way to put a binary frame on the wire, and nothing checks that what you typed is valid JSON first. Incoming binary is not decoded either, only marked as binary, so a protobuf feed proves frames are arriving and tells you nothing about their contents. Timestamps come from your local clock at second resolution, too coarse to order a burst, and the log has no copy, export or clear control — reloading empties it, and reloading also loses it. The address is locked while a connection is open, so changing endpoints means disconnecting first.

Common use cases

  • Confirming a wss:// endpoint is reachable before blaming your own client code
  • Reading the close code a server sends when it rejects a connection
  • Checking whether a chat or notification feed pushes anything after you subscribe
  • Sending a hand-written subscribe frame to a pub/sub or market data endpoint
  • Verifying a proxy or load balancer passes the upgrade through to the app
  • Showing a colleague what the server actually sends rather than describing it

Frequently asked questions

What does WebSocket close code 1006 mean?

1006 is a code the browser invents when the connection died without a close frame, so it never came from the server at all. It means the TCP connection dropped or the handshake never completed: a refused connection, a DNS failure, a certificate the browser will not accept, a proxy that killed an idle socket. Compare it with codes a server actually sends — 1000 normal close, 1001 going away, 1008 policy violation, 1011 internal error, and 4000 to 4999 defined by the application, which is where an expired token usually appears. A 1006 points you at the network and the handshake, not at your message handling.

How do you authenticate a WebSocket connection from a browser?

Not with an Authorization header. The browser API accepts a URL and an optional subprotocol list, so headers are impossible from any page, and three patterns fill the gap. A token in the query string is simplest and leakiest, since URLs land in proxy and server logs; short-lived single-use tickets fetched over HTTP just before connecting are the usual fix. Cookies are attached automatically when the socket is same-site. Or connect anonymously and make the first message an auth frame, with the server closing the socket if it does not arrive within a second or two.

Why can a page not connect to ws://localhost?

Mixed content. A page loaded over https is not allowed to open a plaintext ws:// socket, and the browser blocks the attempt before anything reaches the network, logging it as a mixed content error rather than a connection failure. Localhost is treated as trustworthy for some kinds of request, but browsers have never agreed on whether that exemption covers WebSockets, so the same address can work in one and fail in another. The reliable routes are a certificate on your dev server so the address becomes wss://, or a tunnel that terminates TLS in front of it.

Why does my WebSocket disconnect after about a minute of silence?

Something in the path is timing out an idle connection, and it is rarely your application. Proxies and load balancers close connections that have carried no bytes for a while — nginx read timeouts and cloud load balancer idle timeouts are the usual culprits — and mobile carrier NAT tables expire on their own schedule. The cure is traffic. WebSocket has ping and pong control frames for exactly this, sent on a timer shorter than the tightest timeout in the chain. Browsers answer incoming pings automatically but give scripts no way to send one, so a browser-side keepalive has to be an ordinary application message.

Can a plain WebSocket client talk to a Socket.IO server?

Usually not, because Socket.IO is a protocol layered on top of WebSocket rather than a wrapper around it. Its client starts with HTTP long polling and upgrades afterwards, the URL carries EIO and transport parameters, and each frame is prefixed with an Engine.IO packet type — so a raw connection is either refused at the handshake or delivers text you cannot interpret. The same holds for SignalR, STOMP over WebSocket and GraphQL subscriptions, each of which expects a negotiation message first. Test those with their own client library; a raw client is fair proof only for a server speaking plain WebSocket.

What is the difference between text and binary WebSocket frames?

The protocol has two data frame types and they are distinct on the wire. A text frame must be valid UTF-8 and arrives in JavaScript as a string; a binary frame arrives as a Blob or an ArrayBuffer, depending on the binaryType set on the socket. JSON travels as text, which is why most web APIs use text frames and nothing else. Binary turns up where the payload is already compact — MessagePack, protobuf, CBOR, audio and video chunks — and it cannot be read as characters, so a client that prints strings can only tell you a frame arrived.

Does CORS apply to WebSockets?

No, and that catches people who assume the browser is protecting them. The handshake carries an Origin header the browser sets and a page cannot forge, but there is no preflight and no Access-Control-Allow-Origin check on the response: if the server accepts the upgrade, the socket opens, whatever origin asked for it. The responsibility sits with the server, which has to inspect Origin itself and refuse anything it does not recognise. Skipping that is the cross-site WebSocket hijacking bug — cookies are attached automatically, so any page can open an authenticated socket on your user's behalf.

Where does the traffic go when you test a socket in a browser?

Straight from your browser to the endpoint you typed. The socket is a WebSocket object constructed in the page you are looking at, so frames travel over your own network to your own server, with no intermediary in the middle to log them. Two things follow. The endpoint sees your IP address and an Origin header naming this page, so a server that filters by origin will reject a test your own app would pass — read that as configuration rather than breakage. And the transcript is only state in the tab: closing the tab closes the socket and takes the log with it.