WebSockets を使うと、Cloudflare Workers のサーバーレス関数とリアルタイムに通信できます。このガイドでは、Cloudflare Workers 上の WebSockets の基本を説明します。Workers 関数で WebSocket サーバーを実装する方法と、クライアントとしてそのサーバーに接続して扱う方法の両方を扱います。
WebSockets は、クライアントとオリジンサーバーのあいだで維持される接続です。WebSocket 接続内では、セッションを再確立せずにクライアントとオリジンがデータをやり取りできます。このため、データ交換は高速です。WebSockets は、ライブチャットやゲームなどのリアルタイムアプリケーションによく使われます。
Cloudflare Workers の WebSocket サーバーでは、クライアントからのメッセージをリアルタイムに受け取れます。このガイドでは、Workers で WebSocket サーバーをセットアップする方法を示します。
クライアントはブラウザーで WebSocket の新しいインスタンスを作り、Workers 関数の URL を渡すことで WebSocket リクエストを送れます。
// In client-side JavaScript, connect to your Workers function using WebSockets:
const websocket = new WebSocket(
"wss://example-websocket.signalnerve.workers.dev",
);受信した WebSocket リクエストが Workers 関数に届くと、文字列値 websocket が設定された Upgrade ヘッダーが含まれます。WebSocket のインスタンス化を続ける前に、このヘッダーを確認します。
async function handleRequest(request) {
const upgradeHeader = request.headers.get('Upgrade');
if (!upgradeHeader || upgradeHeader !== 'websocket') {
return new Response('Expected Upgrade: websocket', { status: 426 });
}
}use worker::*;
#[event(fetch)]
async fn fetch(req: HttpRequest, _env: Env, _ctx: Context) -> Result<worker::Response> {
let upgrade_header = match req.headers().get("Upgrade") {
Some(h) => h.to_str().unwrap(),
None => "",
};
if upgrade_header != "websocket" {
return worker::Response::error("Expected Upgrade: websocket", 426);
}
}Upgrade ヘッダーを適切に確認したら、サーバー側とクライアント側の WebSocket を含む WebSocketPair の新しいインスタンスを作成できます。一方の WebSocket は Workers 関数で扱い、もう一方はプロトコル切り替えを示す 101 ステータスコード ↗ 付きの Response の一部として返します。
async function handleRequest(request) {
const upgradeHeader = request.headers.get('Upgrade');
if (!upgradeHeader || upgradeHeader !== 'websocket') {
return new Response('Expected Upgrade: websocket', { status: 426 });
}
const webSocketPair = new WebSocketPair();
const client = webSocketPair[0],
server = webSocketPair[1];
return new Response(null, {
status: 101,
webSocket: client,
});
}use worker::*;
#[event(fetch)]
async fn fetch(req: HttpRequest, _env: Env, _ctx: Context) -> Result<worker::Response> {
let upgrade_header = match req.headers().get("Upgrade") {
Some(h) => h.to_str().unwrap(),
None => "",
};
if upgrade_header != "websocket" {
return worker::Response::error("Expected Upgrade: websocket", 426);
}
let ws = WebSocketPair::new()?;
let client = ws.client;
let server = ws.server;
server.accept()?;
worker::Response::from_websocket(client)
}WebSocketPair コンストラクターは、0 と 1 のキーにそれぞれ WebSocket インスタンスを持つ Object を返します。次の例のように、Object.values ↗ と ES6 の分割代入 ↗ で、このペアから 2 つの WebSocket を取り出すのが一般的です。
Worker 内で client WebSocket との通信を始めるには、server WebSocket で accept を呼び出します。これにより Workers ランタイムは、WebSocket データを待ち受け、client WebSocket との接続を開いたままにします。
async function handleRequest(request) {
const upgradeHeader = request.headers.get('Upgrade');
if (!upgradeHeader || upgradeHeader !== 'websocket') {
return new Response('Expected Upgrade: websocket', { status: 426 });
}
const webSocketPair = new WebSocketPair();
const [client, server] = Object.values(webSocketPair);
server.accept();
return new Response(null, {
status: 101,
webSocket: client,
});
}use worker::*;
#[event(fetch)]
async fn fetch(req: HttpRequest, _env: Env, _ctx: Context) -> Result<worker::Response> {
let upgrade_header = match req.headers().get("Upgrade") {
Some(h) => h.to_str().unwrap(),
None => "",
};
if upgrade_header != "websocket" {
return worker::Response::error("Expected Upgrade: websocket", 426);
}
let ws = WebSocketPair::new()?;
let client = ws.client;
let server = ws.server;
server.accept()?;
worker::Response::from_websocket(client)
}WebSockets は複数の Events を発行し、addEventListener で接続できます。次の例は message イベントにフックし、そのデータで console.log を出力します。
async function handleRequest(request) {
const upgradeHeader = request.headers.get('Upgrade');
if (!upgradeHeader || upgradeHeader !== 'websocket') {
return new Response('Expected Upgrade: websocket', { status: 426 });
}
const webSocketPair = new WebSocketPair();
const [client, server] = Object.values(webSocketPair);
server.accept();
server.addEventListener('message', event => {
console.log(event.data);
});
return new Response(null, {
status: 101,
webSocket: client,
});
}use futures::StreamExt;
use worker::*;
#[event(fetch)]
async fn fetch(req: HttpRequest, _env: Env, _ctx: Context) -> Result<worker::Response> {
let upgrade_header = match req.headers().get("Upgrade") {
Some(h) => h.to_str().unwrap(),
None => "",
};
if upgrade_header != "websocket" {
return worker::Response::error("Expected Upgrade: websocket", 426);
}
let ws = WebSocketPair::new()?;
let client = ws.client;
let server = ws.server;
server.accept()?;
wasm_bindgen_futures::spawn_local(async move {
let mut event_stream = server.events().expect("could not open stream");
while let Some(event) = event_stream.next().await {
match event.expect("received error in websocket") {
WebsocketEvent::Message(msg) => server.send(&msg.text()).unwrap(),
WebsocketEvent::Close(event) => console_log!("{:?}", event),
}
}
});
worker::Response::from_websocket(client)
}import { Hono } from 'hono'
import { upgradeWebSocket } from 'hono/cloudflare-workers'
const app = new Hono()
app.get(
'*',
upgradeWebSocket((c) => {
return {
onMessage(event, ws) {
console.log('Received message from client:', event.data)
ws.send(`Echo: ${event.data}`)
},
onClose: () => {
console.log('WebSocket closed:', event)
},
onError: () => {
console.error('WebSocket error:', event)
},
}
})
)
export default app;Workers 関数と通信する WebSocket クライアントを書く手順は 2 つです。まず WebSocket インスタンスを作成し、次にイベントリスナーを付けます。
const websocket = new WebSocket(
"wss://websocket-example.signalnerve.workers.dev",
);
websocket.addEventListener("message", (event) => {
console.log("Message received from server");
console.log(event.data);
});WebSocket クライアントは send 関数でサーバーにメッセージを返せます。
websocket.send("MESSAGE");WebSocket のやり取りが終わったら、クライアントは close で接続を閉じられます。
websocket.close();実際の例は websocket-template ↗ を参照し、WebSockets を始めてください。
Cloudflare Workers は new WebSocket(url) コンストラクターに対応しています。Worker は、前述のクライアント実装と同じ方法で、リモートサーバーへの WebSocket 接続を確立できます。
加えて、Cloudflare では Upgrade ヘッダーを設定した URL への fetch リクエストでも WebSocket 接続を確立できます。
async function websocket(url) {
// Make a fetch request including `Upgrade: websocket` header.
// The Workers Runtime will automatically handle other requirements
// of the WebSocket protocol, like the Sec-WebSocket-Key header.
let resp = await fetch(url, {
headers: {
Upgrade: "websocket",
},
});
// If the WebSocket handshake completed successfully, then the
// response has a `webSocket` property.
let ws = resp.webSocket;
if (!ws) {
throw new Error("server didn't accept WebSocket");
}
// Call accept() to indicate that you'll be handling the socket here
// in JavaScript, as opposed to returning it on to a client.
// You can pass { allowHalfOpen: true } if you need to coordinate
// the close handshake manually (for example, when proxying).
ws.accept();
// Now you can send and receive messages like before.
ws.send("hello");
ws.addEventListener("message", (msg) => {
console.log(msg.data);
});
}web_socket_auto_reply_to_close 互換フラグ(互換日付が 2026-04-07 以降では既定で有効)では、Workers ランタイムが受信した Close フレームに自動応答し、close イベントを発火する前に readyState を CLOSED に遷移します。close イベントハンドラー内で close() を呼ぶ必要はありません。呼んでも安全です(呼び出しは無視されます)。
ハーフオープン動作が必要な場合(例: WebSocket プロキシ)は、accept() に { allowHalfOpen: true } を渡します。このフラグが有効になると、new WebSocket(url) は常に自動応答します。クライアント WebSocket でハーフオープン動作を得るには、前述の fetch() ベースのパターンを使い、ws.accept({ allowHalfOpen: true }) を呼び出します。
詳細は WebSocket の close 動作 を参照してください。
Cloudflare Workers は WebSocket 圧縮に対応しています。詳細は WebSocket Compression を参照してください。