Build a WebSocket server with WebSocket ·续
// Upon receiving a message from the client, the server replies with the same message, the session ID of the connection,
// and the total number of connections with the "[Durable Object]: " prefix
ws.send([Durable Object] message: ${message}, from: ${session.id}, to: the initiating client. Total connections: ${this.sessions.size});
// Send a message to all WebSocket connections, loop over all the connected WebSockets.
this.sessions.forEach((attachment, connectedWs) => {
connectedWs.send([Durable Object] message: ${message}, from: ${session.id}, to: all clients. Total connections: ${this.sessions.size});
});
// Send a message to all WebSocket connections except the connection (ws),
// loop over all the connected WebSockets and filter out the connection (ws).
this.sessions.forEach((attachment, connectedWs) => {
if (connectedWs !== ws) {
connectedWs.send([Durable Object] message: ${message}, from: ${session.id}, to: all clients except the initiating client. Total connections: ${this.sessions.size});
}
});
}
async webSocketClose(ws: WebSocket, code: number, reason: string, wasClean: boolean) { // With web_socket_auto_reply_to_close (compat date >= 2026-04-07), the runtime // auto-replies to Close frames. Calling close() is safe but no longer required. ws.close(code, reason); this.sessions.delete(ws); } }
```py
from workers import DurableObject, Response, WorkerEntrypoint
from js import WebSocketPair, WebSocketRequestResponsePair
import uuid
class Session:
def __init__(self, *, ws):
self.ws = ws
# Worker
class Default(WorkerEntrypoint):
async def fetch(self, request):
if request.url.endswith('/websocket'):
# Expect to receive a WebSocket Upgrade request.
# If there is one, accept the request and return a WebSocket Response.
upgrade_header = request.headers.get('Upgrade')
if not upgrade_header or upgrade_header != 'websocket':
return Response('Worker expected Upgrade: websocket', status=426)
if request.method != 'GET':
return Response('Worker expected GET method', status=400)
# Since we are hard coding the Durable Object ID by providing the constant name 'foo',
# all requests to this Worker will be sent to the same Durable Object instance.
stub = self.env.WEBSOCKET_HIBERNATION_SERVER.getByName("foo")
return await stub.fetch(request)
return Response(
"""Supported endpoints:
/websocket: Expects a WebSocket upgrade request""",
status=200,
headers={'Content-Type': 'text/plain'}
)
# Durable Object
class WebSocketHibernationServer(DurableObject):
def __init__(self, ctx, env):
super().__init__(ctx, env)
# Keeps track of all WebSocket connections, keyed by session ID
# When the DO hibernates, gets reconstructed in the constructor
self.sessions = {}
# As part of constructing the Durable Object,
# we wake up any hibernating WebSockets and
# place them back in the `sessions` map.
# Get all WebSocket connections from the DO
for ws in self.ctx.getWebSockets():
attachment = ws.deserializeAttachment()
if attachment:
# If we previously attached state to our WebSocket,
# let's add it to `sessions` map to restore the state of the connection.
# Use the session ID as the key
self.sessions[attachment] = Session(ws=ws)
# Sets an application level auto response that does not wake hibernated WebSockets.
self.ctx.setWebSocketAutoResponse(WebSocketRequestResponsePair.new('ping', 'pong'))
async def fetch(self, request):
# Creates two ends of a WebSocket connection.
client, server = WebSocketPair.new().object_values()
# Calling `acceptWebSocket()` informs the runtime that this WebSocket is to begin terminating
# request within the Durable Object. It has the effect of "accepting" the connection,
# and allowing the WebSocket to send and receive messages.
# Unlike `ws.accept()`, `this.ctx.acceptWebSocket(ws)` informs the Workers Runtime that the WebSocket
# is "hibernatable", so the runtime does not need to pin this Durable Object to memory while
# the connection is open. During periods of inactivity, the Durable Object can be evicted
# from memory, but the WebSocket connection will remain open. If at some later point the
# WebSocket receives a message, the runtime will recreate the Durable Object
# (run the `constructor`) and deliver the message to the appropriate handler.
self.ctx.acceptWebSocket(server)
# Generate a random UUID for the session.
id = str(uuid.uuid4())
# Attach the session ID to the WebSocket connection and serialize it.
# This is necessary to restore the state of the connection when the Durable Object wakes up.
server.serializeAttachment(id)
# Add the WebSocket connection to the map of active sessions, keyed by session ID.
self.sessions[id] = Session(ws=server)
return Response(None, status=101, web_socket=client)
async def webSocketMessage(self, ws, message):
# Get the session ID associated with the WebSocket connection.
session_id = ws.deserializeAttachment()
# Upon receiving a message from the client, the server replies with the same message, the session ID of the connection,
# and the total number of connections with the "[Durable Object]: " prefix
ws.send(f"[Durable Object] message: {message}, from: {session_id}, to: the initiating client. Total connections: {len(self.sessions)}")
# Send a message to all WebSocket connections, loop over all the connected WebSockets.
for session in self.sessions.values():
session.ws.send(f"[Durable Object] message: {message}, from: {session_id}, to: all clients. Total connections: {len(self.sessions)}")
# Send a message to all WebSocket connections except the connection (ws),
# loop over all the connected WebSockets and filter out the connection (ws).
for session in self.sessions.values():
if session.ws != ws:
session.ws.send(f"[Durable Object] message: {message}, from: {session_id}, to: all clients except the initiating client. Total connections: {len(self.sessions)}")
async def webSocketClose(self, ws, code, reason, wasClean):
# With web_socket_auto_reply_to_close (compat date >= 2026-04-07), the runtime
# auto-replies to Close frames. Calling close() is safe but no longer required.
ws.close(code, reason)
# Get the session ID from the WebSocket attachment to remove it from sessions
session_id = ws.deserializeAttachment()
if session_id:
self.sessions.pop(session_id, None)
Finally, configure your Wrangler file to include a Durable Object binding and migration based on the namespace and class name chosen previously.
{
"$schema": "./node_modules/wrangler/config-schema.json",
"name": "websocket-hibernation-server",
"main": "src/index.ts",
"upload_source_maps": true,
"durable_objects": {
"bindings": [
{
"name": "WEBSOCKET_HIBERNATION_SERVER",
"class_name": "WebSocketHibernationServer"
}
]
},
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": [
"WebSocketHibernationServer"
]
}
]
}
"$schema" = "./node_modules/wrangler/config-schema.json"
name = "websocket-hibernation-server"
main = "src/index.ts"
upload_source_maps = true
[[durable_objects.bindings]]
name = "WEBSOCKET_HIBERNATION_SERVER"
class_name = "WebSocketHibernationServer"
[[migrations]]
tag = "v1"
new_sqlite_classes = [ "WebSocketHibernationServer" ]
Related resources
Was this helpful?
YesNo
On this page
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/durable-objects/examples/websocket-hibernation-server/#page","headline":"Build a WebSocket server with WebSocket Hibernation","description":"Build a WebSocket server using WebSocket Hibernation on Durable Objects and Workers.","url":"https://developers.cloudflare.com/durable-objects/examples/websocket-hibernation-server/","inLanguage":"en","image":"https://developers.cloudflare.com/durable-objects/examples/websocket-hibernation-server/og.png?v=7877994f4d9ae82b","dateModified":"2026-10-12","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"},"keywords":["WebSockets"]}
description: Build a WebSocket server using Durable Objects and Workers. title: Build a WebSocket server image: https://developers.cloudflare.com/durable-objects/examples/websocket-server/og.png?v=efbb7da0ed7ce51b
Documentation Index
Fetch the complete documentation index at: https://developers.cloudflare.com/durable-objects/llms.txt
Use this file to discover all available pages before exploring further.
Docs