Skip to content

非公式本サイトは非公式の日本語ドキュメントであり、Cloudflare 公式サイトではありません。最新情報はdevelopers.cloudflare.comをご確認ください。

WebSockets API を使う

WebSockets API を使い、Cloudflare Workers とリアルタイムに通信します。

最終更新 Markdown で表示Agent セットアップ

WebSockets を使うと、Cloudflare Workers のサーバーレス関数とリアルタイムに通信できます。このガイドでは、Cloudflare Workers 上の WebSockets の基本を説明します。Workers 関数で WebSocket サーバーを実装する方法と、クライアントとしてそのサーバーに接続して扱う方法の両方を扱います。

WebSockets は、クライアントとオリジンサーバーのあいだで維持される接続です。WebSocket 接続内では、セッションを再確立せずにクライアントとオリジンがデータをやり取りできます。このため、データ交換は高速です。WebSockets は、ライブチャットやゲームなどのリアルタイムアプリケーションによく使われます。

WebSocket サーバーを実装する

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 コンストラクターは、01 のキーにそれぞれ WebSocket インスタンスを持つ Object を返します。次の例のように、Object.valuesES6 の分割代入 で、このペアから 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;

クライアントから WebSocket サーバーに接続する

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 を始めてください。

WebSocket クライアントを実装する

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);
	});
}

WebSocket の close 動作

web_socket_auto_reply_to_close 互換フラグ(互換日付が 2026-04-07 以降では既定で有効)では、Workers ランタイムが受信した Close フレームに自動応答し、close イベントを発火する前に readyStateCLOSED に遷移します。close イベントハンドラー内で close() を呼ぶ必要はありません。呼んでも安全です(呼び出しは無視されます)。

ハーフオープン動作が必要な場合(例: WebSocket プロキシ)は、accept(){ allowHalfOpen: true } を渡します。このフラグが有効になると、new WebSocket(url) は常に自動応答します。クライアント WebSocket でハーフオープン動作を得るには、前述の fetch() ベースのパターンを使い、ws.accept({ allowHalfOpen: true }) を呼び出します。

詳細は WebSocket の close 動作 を参照してください。

WebSocket 圧縮

Cloudflare Workers は WebSocket 圧縮に対応しています。詳細は WebSocket Compression を参照してください。

役に立ちましたか?