Handling WebSockets: Real-time Communication in Cloudflare Workers
Learn to implement WebSockets in Cloudflare Workers to enable low-latency, real-time communication. Master connection handling, messaging, and state management.

Previously in this course, we explored queueing tasks to handle background work. Today, we shift from asynchronous processing to synchronous, full-duplex communication by implementing WebSockets in our Workers.
While HTTP is request-response based, WebSockets provide a persistent connection between the client and the server. This is essential for features like live notifications, chat, or collaborative tools where you need near-instant data flow.
Understanding the WebSocket Lifecycle
A WebSocket connection starts with a standard HTTP request containing an Upgrade header. If the server accepts it, the connection "upgrades" from HTTP to the WebSocket protocol. In Cloudflare Workers, this is handled via the WebSocketPair API.
The lifecycle consists of three main phases:
- The Handshake: The client sends an HTTP request; the Worker responds with a
101 Switching Protocolsstatus. - Communication: Data flows bidirectionally as messages (text or binary).
- Closure: Either party terminates the connection, triggering a close event.
Worked Example: A Real-time Echo Server
To get started, we need to initialize a WebSocketPair. One end of this pair remains in the Worker to handle server-side logic, while the other is returned to the client.
JAVASCRIPTexport default { async fetch(request) { // 1. Check for the Upgrade header const upgradeHeader = request.headers.get("Upgrade"); if (upgradeHeader !== "websocket") { return new Response("Expected websocket", { status: 426 }); } // 2. Create the WebSocket pair const webSocketPair = new WebSocketPair(); const [client, server] = Object.values(webSocketPair); // 3. Initialize the server-side socket server.accept(); // 4. Handle incoming messages server.addEventListener("message", (event) => { console.log(CE9178">`Received: ${event.data}`); server.send(CE9178">`Echo: ${event.data}`); }); // 5. Handle connection closure server.addEventListener("close", () => { console.log("Client disconnected"); }); // 6. Return the client end to the browser return new Response(null, { status: 101, webSocket: client, }); }, };
Managing Connection State
In a real application, you rarely just "echo" messages. You often need to track who is connected. Because Workers are stateless and ephemeral, you cannot store server objects in global variables if you want to broadcast messages across multiple instances.
For complex state, you would typically use KV Storage to track active user IDs or session tokens, ensuring that your real-time API remains consistent across the edge network.
Hands-on Exercise: Implementing a Broadcast
Modify the code above to maintain a list of active sockets.
- Create a
Setto store yourserversocket instances. - When a message arrives, iterate through the
Setand callsocket.send()on every connected client. - Remove the socket from the
Setwhen thecloseevent fires.
Note: Be aware that if your Worker scales to multiple isolates, this local Set will only contain connections currently handled by that specific instance.
Common Pitfalls
- Forgetting
server.accept(): If you don't call this, the socket will remain in a pending state and will not process messages. - Idle Timeouts: Cloudflare Workers have execution time limits. While a WebSocket connection can stay open, the Worker instance handling it must remain "alive." If the connection is inactive for too long, the connection may drop.
- CORS Issues: Ensure that if your frontend is on a different domain, you handle it correctly. While WebSockets are not restricted by standard CORS, you should still understand Handling CORS in Workers for the initial handshake request.
FAQ
Can I broadcast to all users globally?
Not with a simple Set. To broadcast globally, you would need a Durable Object, which allows you to maintain state across connections and coordinate messages between clients.
Are WebSockets expensive on Workers? WebSockets consume concurrent connection resources. Keep an eye on your plan limits regarding concurrent connections if you expect high traffic.
How do I handle authentication?
Validate the authentication token in the initial fetch request before calling new WebSocketPair(). If the token is invalid, return a 401 Unauthorized response.
Recap
We've covered the basics of initializing WebSockets, managing the message event loop, and ensuring clean connection termination. This real-time capability allows your Workers to become highly interactive backends.
Up next: We will explore Cloudflare Access Basics to protect these real-time endpoints.
Work with me

Custom Email & File Storage System on Cloudflare (Google Workspace Alternative)
Your own private email + file storage suite on your domain โ unlimited mailboxes, no per-seat fees. A self-owned Google Workspace alternative for a flat ~$5/month.

React & Next.js Dashboard / Admin UI Development
A clean, data-rich dashboard UI in React or Next.js โ charts, tables, and real-time data that your users will actually enjoy using.


