NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
npm · #2911 most downloaded on npm
Cross-platform WebSocket Servers for Node.js, Deno, Bun and Cloudflare Workers
Last release 1 months ago
20 Aug 2026
Release timing varies
gaps range from 2 weeks to 8 months
Nearly every release is documented
notes for 28 of 30 stable releases
Nothing withdrawn
no release was ever pulled
3 years old
30 releases · first in 2023
Resolve hooks attached to the request via Symbol.for("crossws.hooks")
One column per month.
Full Changelog : v0.4.10...v0.4.11
Full Changelog: v0.4.10...v0.4.11
proxy: Add clientIdleTimeout to reclaim silent clients
clientIdleTimeout to reclaim silent clients (#204)server: Default resolve to app fetch .crossws
resolve to app fetch .crossws (#200)idleTimeout option (#201)proxy: Support unix socket targets out of the box
proxy: Support async target resolver in createWebSocketProxy ( #196 ) docs
createWebSocketProxy (#196) docsremoteAddress eagerly at upgrade time (29d8519)remoteAddr before upgrading the request (af5fe64)Add vercel adapter (internal testing)
vercel adapter (internal testing) (#191)forwardProtocol to rewrite the upstream subprotocol (#190)ws upgraded from 8.20.0 to 8.21.0New createWebSocketProxy util returns crossws hooks that forward every incoming peer to an upstream ws:// or wss:// target. Text and binary messages,
createWebSocketProxy util (#184)New createWebSocketProxy util returns crossws hooks that forward every incoming peer to an upstream ws:// or wss:// target. Text and binary messages, close, and error events are relayed bi-directionally, and client messages sent before the upstream is ready are buffered (up to a configurable maxBufferSize) and flushed once connected.
import crossws from "crossws/adapters/<adapter>";
import { createWebSocketProxy } from "crossws";
const ws = crossws({
hooks: createWebSocketProxy("wss://echo.websocket.org"),
});The target can also be a (peer) => url resolver for dynamic routing, and the returned hooks can be extended (for auth, logging, header forwarding, etc.) like any other crossws hooks. See the proxy guide for auth, dynamic targets, and SSRF hardening notes.
fromNodeUpgradeHandler util + socket.io support (#185)New fromNodeUpgradeHandler util (exported from crossws/adapters/node) wraps a plain Node.js (req, socket, head) upgrade handler as a crossws hooks object. This makes it possible to mount existing ws, socket.io, or express-ws based WebSocket servers through the Node adapter while they keep full ownership of the socket lifecycle.
import { WebSocketServer } from "ws";
import { fromNodeUpgradeHandler } from "crossws/adapters/node";
import { serve } from "crossws/server/node";
const wss = new WebSocketServer({ noServer: true });
wss.on("connection", (ws) => {
ws.on("message", (data) => ws.send(data));
});
serve({
fetch: () => new Response("ok"),
websocket: fromNodeUpgradeHandler((req, socket, head) => {
wss.handleUpgrade(req, socket, head, (ws) => {
wss.emit("connection", ws, req);
});
}),
});Because the wrapped handler takes ownership of the socket, crossws lifecycle hooks (open/message/close/error) are not invoked for connections routed through it — the underlying library manages them. A full socket.io example combining WebSocket and HTTP long-polling transports is included in the repo.
bunny adapter (#179)ServerWithWSOptions and WSOptions types (#180)EADDRINUSE handling (#183)createWebSocketProxy util (#184)bunny adapter (#179)fromNodeUpgradeHandler util + socket.io example (#185)ServerWithWSOptions and WSOptions types (#180)EADDRINUSE handling (#183)Use AbortController for StubRequest.signal
AbortController for StubRequest.signal (#175)compare changes 📦 Build Migrate to obuild (rolldown)
Mention publish not broadcasting to sender
start command (7ab898c)node server: Properly pass request to NodeRequest
NodeRequest (28e5d64)start command (7ab898c)Previously, you had to use manual adapter integrations or listhen for setting up a functional websocket server.
Previously, you had to use manual adapter integrations or listhen for setting up a functional websocket server.
Powered by our new standard server building block srvx (experimental) and conditional exports, you can write a single server entry that magically works and natively integrates with any supported runtime.
// Magically works with Bun, Deno, and Node.js!
// (also Cloudflare or SSE as fallback)
import { serve } from "crossws/server";
serve({
websocket: {
message: console.log
}
});
Relevant PR: #162
In the previous versions of crossws, there was one peers global Set in the server. Publishing to the same "topic", published to all peers subscribed to the same topic, even if they were connected to different WebSocket endpoints (as a result, we had to make sure topics are unique).
While it works in simple servers with a single set of hooks, when using dynamic hooks (using resolver), it makes sense that each peer only subscribes and publishes to relevant peers, and topics don't overlap.
With the new version, connected websocket peers have an assigned namespace. By default, the request "pathname" is used for namespace, so peers connected to /_ws/room1 and /_ws/room2 are isolated from each other.
It is possible to have customized namespace logic based on request using the adapter option getNamespace: (req: Request) => "<namespace>". The default behavior is req => new URL(req.url).pathname. You can use () => "default" for behavior similar to previous versions.
It is also possible to use a dynamically determined namespace per upgrade connection by returning { namespace } from the upgrade(req) hook.
Global ws.publish(topic, message) still publishes to all peers, but can specify a namespace using ws.publish(topic, message, { namespace })
cloudflare and cloudflare-durable adapters are merged (#165) into single cloudflare. In case a Durable object is not exported or not resolvable, crossws will fallback to a simple WebSocket upgrade without pub/sub support.
With this release, Cloudflare (durable) also supports global publish() via RPC (#166). With this enhancement, all adapters now support global publish!
sec-webSocket-protocol header (#142)Request as first param to resolve (#160)Response is returned (#164)Request interface (#156)PeerContext interface for type augmentation (#159)upgrade hook (d843cd0)unjs to h3js (#155)PeerContext type (#161)PeerContext interface for type augmentation (#159)upgrade hook (#163)sec-webSocket-protocol (#142)uncrypto dependency (#153)Request as first param to resolve (#160)upgrade return type (d843cd0)upgrade if Response is returned (#164)cloudflare and cloudflare-durable adapters (#165)h3js from unjs (#155)PeerContext type (#161)upgrade hook (#163)sec-webSocket-protocol (#142)Request as first param to resolve (#160)upgrade if Response is returned (#164)cloudflare and cloudflare-durable adapters (#165)node: Support closeAll with force flag
closeAll with force flag (#147)AdapterInternal type (#149)NodeAdapter.handleUpgrade as async (#136)closeAll with force flag (#147)AdapterInternal type (#149)NodeAdapter.handleUpgrade as async (#136)cloudflare: Support resolveDurableStub
resolveDurableStub (#130)request.context as read-only (#133)code and reason to close hook (#132)upgrade === "websocket" in example (#131)resolveDurableStub (#130)uint8Array() (#128)code and reason to close hook (#132)request.context as read only (#133)upgrade === "websocket" in example (#131)Allow throwing error with .response prop in upgrade
.response prop in upgrade (#113)> 🔑 This release includes enhancements to make authentication and session handling easier.
[!NOTE] 🔑 This release includes enhancements to make authentication and session handling easier.
request param in the upgrade hook and throw a Response as an error to terminate the upgrade (#91)request.context in the upgrade hook and from peer.context in the other hooks to preserve session data (#110, #111 (Note: context can be volatile in some environments like cloudflare-durable)peer.request always has .headers if defined (e915f8d)peer.request as always defined (8fbb59b)upgrade hook (#91)context (#110)upgrade hook and peer (#111)peer.request always has .headers if defined (e915f8d)peer.request as always defined (8fbb59b)compare changes ### 🩹 Fixes - types: AdapterOptions type (#80) ### ❤️ Contributors - Hayatosc (@hayatosc)
…with peer.request.url and peer.request.headers (breaking change), and peer.addr is also renamed to peer.remoteAddress to improve readability (breaking…
[!IMPORTANT] Since this is a major version, ecosystem migration takes time. Stay tuned for listhen, h3 and nitro dependency upgrades.
Crossws 0.3.x includes an overhaul of refactors, stability improvements, and new features. A new codebase and testing matrix had been implemented (#55) to make sure all supported adapters and runtimes work as expected and are consistent with each other.
The peer object allows easy interaction with connected WebSocket clients from server route hooks (peer docs).
To improve Web standards compatibility, accessing upgrade URL and headers is now possible with peer.request.url and peer.request.headers (breaking change), and peer.addr is also renamed to peer.remoteAddress to improve readability (breaking change) and support is increased across providers. You can also use new lazy-generated and secure peer.id (UUID v4) for various purposes including temporary sessions or persistent state.
Two new methods are now supported to close connected peers using peer.close(code, reason) and peer.terminate(). With this new version, you can access a standard WebSocket interface using peer.websocket.
[!NOTE] Today many of the server runtimes don't provide a spec-compliant
WebSocketAPI. Crossws uses an internal proxy to polyfill consistent access toextensions,protocol, andreadyState. See compatibility table for more details.
On message hook, you receive a message object containing data from the client (message docs).
Parsing incoming messages can be tricky across runtimes. Message object now has stable methods .text(), .json(), .uint8Array(), .arrayBuffer(), .blob() to safely read message as desired format. If you need, you can also access .rawData, .peer, .event (if available), and lazy generated secure UUID v4 .id
upgrade hookWhen you need to authenticate and validate WebSocket clients before they can upgrade, you can now easily use the upgrade hook to check incoming URLs and headers/cookies and return a Web Standard Response in case you need to abort the upgrade.
One of the common use cases of WebSockets is pubsub. This release adds pub-sub support to Deno provider and also you can globally broadcast messages using ws.publish for advanced use cases.
Normally with cloudflare workers, it is not possible to connect multiple peers with each other. Cloudflare Durable Objects (available on paid plans) allows building collaborative editing tools, interactive chat, multiplayer games, and applications that need coordination among multiple clients.
Crossws provides a new composable method to easily integrate WebSocket handlers with Durable Objects. Hibernation is supported out of the box to reduce billing costs when connected clients are inactive. (durable object peer docs)
peer.close() and peer.terminate() support (#36)ws.publish (#61)peer.ctx to peer._internal (#59)#22, 76fc105, 7dacb00, #46, #45, #44, a96dca3, 898ab49, 2e49cc3
WebSocket and EventSource (#67)compare changes ### 🚀 Enhancements - Auto generated peer id (a3b61f5) - Basic pubsub support for node (4bd61ca) ### 💅 Refactors - Improve peer inspect
node: Respect x-forwarded for client id
compare changes ### 🩹 Fixes - deno: Pass info (2c63b37) ### 🏡 Chore - example: Handle secure origins (7f8639f) ### ❤️ Contributors - Pooya Parsa (@pi0
$callHook should check hook existence
compare changes #### ⚠️ Breaking Changes - ⚠️ Improve types and api (2ebacd3) ### ❤️ Contributors - Pooya Parsa (@pi0)
compare changes ### 🏡 Chore - Add build script to release (6681afa) ### ❤️ Contributors - Pooya Parsa (@pi0)
Allow access to peer url and headers
bun and uws (a486f45)uWebSockets.js peer dep (1ef0585)Import bun types from @types/bun
Nothing published for this version
### 🏡 Chore - Update readme (af705a6) - Don't gitignore manual types (ec9330b) ### ❤️ Contributors - Pooya Parsa (@pi0)
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →