WebSockets
WebSockets allow you to communicate in real time with your Cloudflare Workers serverless functions. For a complete example, refer to Using the WebSockets API.
// { 0: <WebSocket>, 1: <WebSocket> }let websocketPair = new WebSocketPair();The WebSocketPair returned from this constructor is an Object, with two WebSockets at keys 0 and 1.
These WebSockets are commonly referred to as client and server. The below example combines Object.values and ES6 destructuring to retrieve the WebSockets as client and server:
let [client, server] = Object.values(new WebSocketPair());-
accept(options?)- Accepts the WebSocket connection and begins terminating requests for the WebSocket on Cloudflare's global network. This effectively enables the Workers runtime to begin responding to and handling WebSocket requests.
-
optionsobject optional-
An optional configuration object with the following properties:
allowHalfOpenboolean optional — Whentrue, the runtime will not automatically send a reciprocal Close frame when a Close frame is received from the peer. Instead,readyStateremainsCLOSINGuntil you explicitly callclose(). This is useful for WebSocket proxying where you need to coordinate the close across both sides of the proxy. Defaults tofalse.
-
-
addEventListener(eventWebSocketEvent, callbackFunctionFunction)- Add callback functions to be executed when an event has occurred on the WebSocket.
-
eventWebSocketEvent- The WebSocket event (refer to Events) to listen to.
-
callbackFunction(messageMessage)Function- A function to be called when the WebSocket responds to a specific event.
-
close(codenumber, reasonstring)- Close the WebSocket connection.
-
codeintegeroptional- An integer indicating the close code sent by the server. This should match an option from the list of status codes ↗ provided by the WebSocket spec.
-
reasonstringoptional- A human-readable string indicating why the WebSocket connection was closed.
-
send(messagestring | ArrayBuffer | ArrayBufferView)- Send a message to the other WebSocket in this WebSocket pair.
-
messagestring- The message to send down the WebSocket connection to the corresponding client. This should be a string or something coercible into a string; for example, strings and numbers will be simply cast into strings, but objects and arrays should be cast to JSON strings using
JSON.stringify, and parsed in the client.
- The message to send down the WebSocket connection to the corresponding client. This should be a string or something coercible into a string; for example, strings and numbers will be simply cast into strings, but objects and arrays should be cast to JSON strings using
-
readyStatenumber-
Returns the current state of the WebSocket connection. Possible values:
Constant Value Description WebSocket.CONNECTING0The connection is not yet open. WebSocket.OPEN1The connection is open and ready to communicate. WebSocket.CLOSING2The connection is in the process of closing. WebSocket.CLOSED3The connection is closed.
-
-
binaryTypestring- Controls how binary frames received on this WebSocket are surfaced to the
messageevent. Valid values are"blob"and"arraybuffer". The value is consulted when each incoming binary frame is dispatched, so assigning a new value affects subsequent messages only. The default is controlled by thewebsocket_standard_binary_typecompatibility flag. Refer to Binary messages for details.
- Controls how binary frames received on this WebSocket are surfaced to the
-
close- An event indicating the WebSocket has closed. The
CloseEventincludescode(number),reason(string), andwasClean(boolean) properties.
- An event indicating the WebSocket has closed. The
-
error- An event indicating there was an error with the WebSocket.
-
message- An event indicating a new message received from the client, including the data passed by the client.
dataany - The data passed back from the other WebSocket in your pair.typestring - Defaults tomessage.
With the web_socket_auto_reply_to_close compatibility flag (enabled by default on compatibility dates on or after 2026-04-07), the Workers runtime automatically sends a reciprocal Close frame when it receives a Close frame from the peer. The readyState transitions to CLOSED before the close event fires. This matches the WebSocket specification ↗ and standard browser behavior.
If you still call close() inside the close event handler, the call is silently ignored. Existing code that manually replies to Close frames will continue to work without changes.
server.addEventListener("close", (event) => { // readyState is already CLOSED — no need to call server.close(). console.log(server.readyState); // WebSocket.CLOSED console.log(event.code); // 1000 console.log(event.wasClean); // true});The automatic close behavior can interfere with WebSocket proxying, where a Worker sits between a client and a backend and needs to coordinate the close on both sides independently. To support this, pass { allowHalfOpen: true } to accept():
server.accept({ allowHalfOpen: true });
server.addEventListener("close", (event) => { // readyState is still CLOSING here, giving you time // to coordinate the close on the other side. console.log(server.readyState); // WebSocket.CLOSING
// Manually close when ready. server.close(event.code, "done");});On compatibility dates before 2026-04-07 (or with the web_socket_manual_reply_to_close flag), receiving a Close frame leaves the WebSocket in CLOSING state, and your code must call close() to complete the handshake. Failing to do so can result in 1006 abnormal closure errors on the client.
WebSocket frames carry either text or binary payloads, and the choice between the two is made by the sender at the time the frame is sent. Text frames are always delivered to the message event as JavaScript strings. Binary frames are delivered either as Blob ↗ or as ArrayBuffer ↗, depending on the WebSocket's binaryType.
With the websocket_standard_binary_type compatibility flag (enabled by default on compatibility dates on or after 2026-03-17), binaryType defaults to "blob" and binary frames are delivered as Blob objects. This matches the WebSocket specification ↗ and standard browser behavior. Without the flag, binaryType defaults to "arraybuffer" and binary frames are delivered as ArrayBuffer, matching the runtime's historical behavior.
The binaryType property itself is always available. To opt back into ArrayBuffer delivery for a single WebSocket, assign binaryType before calling accept():
const resp = await fetch("https://example.com", { headers: { Upgrade: "websocket" },});const ws = resp.webSocket;
// Opt back into ArrayBuffer delivery for this WebSocket.ws.binaryType = "arraybuffer";ws.accept();
ws.addEventListener("message", (event) => { if (typeof event.data === "string") { // Text frame. } else { // event.data is an ArrayBuffer because we set binaryType above. }});An incoming binary frame is fully buffered before the message event fires, regardless of binaryType. The choice between Blob and ArrayBuffer does not change when or whether the frame is received — only how you access its bytes:
- With
"arraybuffer",event.datais anArrayBuffer↗. You can inspect its size and read bytes synchronously (for example,new Uint8Array(event.data)). - With
"blob",event.datais aBlob↗. Reading the bytes is asynchronous — for example,await event.data.arrayBuffer()orawait event.data.bytes().
Under the new default, a binary message handler must be async in order to read the payload. If you want to keep an existing synchronous handler, set binaryType to "arraybuffer" on the WebSocket.
Per the WebSocket specification ↗, binaryType is mutable: the value is consulted at the moment each binary frame is dispatched to the message event, so assigning a new value affects only subsequent messages. If you want every binary message on a WebSocket to be delivered as the same type, assign binaryType before calling accept(). That guarantees the setting is in place before the runtime starts dispatching any incoming frames.
If you are not ready to migrate and want to keep ArrayBuffer as the default for every WebSocket in your Worker, add the no_websocket_standard_binary_type flag to your Wrangler configuration file. Individual WebSockets can still override the default by assigning binaryType.