WebSockets now automatically reply to Close frames
The Workers runtime now 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.
This change is enabled by default for Workers using compatibility dates on or after 2026-04-07 (via the web_socket_auto_reply_to_close compatibility flag). Existing code that manually calls close() inside the close event handler will continue to work — the call is silently ignored when the WebSocket is already closed.
const [client, server] = Object.values(new WebSocketPair());server.accept();
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 use case, pass { allowHalfOpen: true } to accept():
const [client, server] = Object.values(new WebSocketPair());
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");});For more information, refer to WebSockets Close behavior.