#このページで確認できること

実装上の制約: エコーサーバーはローカル観測専用としてリポジトリに同梱しています(demos/websocket-node/)。公開環境では起動しない方針です。docker compose up websocket で起動し、ws://localhost:9001 に接続できます。

#ハンドシェイク

クライアントは通常のHTTPリクエストに Upgrade 系ヘッダーを付けて送り、サーバーが 101 Switching Protocols を返した瞬間からプロトコルが切り替わります。

request
GET /ws HTTP/1.1 Host: example.com Connection: Upgrade Upgrade: websocket Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Sec-WebSocket-Version: 13
response
HTTP/1.1 101 Switching Protocols Connection: Upgrade Upgrade: websocket Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

#通信シーケンス

WebSocket handshake と双方向通信
participants: Client, Server
Client -> Server: GET /ws HTTP/1.1 + Upgrade: websocket
Server --> Client: HTTP/1.1 101 Switching Protocols
note: ここからはHTTPではなくWebSocketフレーム
Client -> Server: text frame "hello"
Server -> Client: text frame "world" (サーバー起点の送信が可能)
Server -> Client: ping frame
Client --> Server: pong frame
Client -> Server: close frame
Server --> Client: close frame
プロキシ / TLSインスペクション経由の WebSocket
participants: Client, Proxy, Server
Client -> Proxy: GET /ws + Upgrade headers
Proxy -> Server: Upgrade リクエストを転送
Server --> Proxy: 101 Switching Protocols
Proxy --> Client: 101 Switching Protocols
note: 以降プロキシは双方向ストリームを素通しできる必要がある
Client <-> Server: WebSocket frames (via proxy)
プロキシ通過性は重要な確認点: Upgrade は hop-by-hop です。HTTPを終端して再転送するリバースプロキシ・ロードバランサ・インスペクション装置は、WebSocket Upgradeと以降の双方向通信を扱える必要があります。一方、WSSを不透明なTCPトンネルとして中継するフォワードプロキシは、WebSocketフレーム自体を理解しなくても転送できます。nginxからHTTP/1.1で上流へリバースプロキシする構成では、proxy_http_version 1.1; と適切な Upgrade / Connection ヘッダー転送が必要です。経路上の設定によっては101が返らない、またはidle timeoutで切断されます。

#検証コマンド

デモサーバーの起動 (リポジトリ同梱)
$ docker compose up websocket
# ws://localhost:9001 でエコーサーバーが起動する (受信メッセージをそのまま返す)
curl — ハンドシェイクだけを観測
# Upgrade ヘッダーを手動で付けて 101 を確認する(フレーム交換はしない)
$ curl -i -N \
    -H 'Connection: Upgrade' \
    -H 'Upgrade: websocket' \
    -H 'Sec-WebSocket-Version: 13' \
    -H 'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==' \
    http://localhost:9001/

# curl 7.86+ なら ws:// スキームでハンドシェイクし、受信フレームを観測できる
$ curl --include --no-buffer ws://localhost:9001/
expected response
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
ブラウザ — DevTools Console
const ws = new WebSocket("ws://localhost:9001/");
ws.onopen  = () => ws.send("hello");
ws.onmessage = (e) => console.log("recv:", e.data);  // → recv: hello
デモサーバーの制限: 同梱のエコーサーバーはローカル観測専用で、公開環境では起動しません。メッセージサイズ上限64KB、同時接続50、30秒ごとのpingにpongが返らない接続の切断、ペイロード非記録を実装しています。方針は docs/demos-local-only.md を参照してください。なお HTTPS で配信されたページからは ws://localhost への接続がブラウザにブロックされる場合があります(混在コンテンツ)。その場合はローカル配信(http://localhost:8080)のページから試してください。

#観測ポイント

curl101 が返るか、Sec-WebSocket-Accept の値が正しいか。200 や 400 が返る場合は中間装置がUpgradeを処理できていない可能性。
DevToolsNetwork → WS フィルタ。ハンドシェイクのヘッダーと、Messages タブで送受信方向やメッセージ、制御フレームなどを確認できる。フラグメントされたデータの表示単位、色、表示名はブラウザとバージョンにより変わり得る。
Proxy101 がクライアントまで届くか。長時間接続が idle timeout(60秒等)で切られないか。Connection/Upgrade ヘッダーがプロキシで落とされていないか。
tcpdump / Wireshark平文なら websocket フィルタでフレーム解析可能。ハンドシェイクはHTTPとして、以降はWebSocketフレームとしてデコードされる。マスクの有無も見える。
TLS inspection装置がWebSocketを認識して素通しするか、切断するかは製品・設定差が大きい。direct で動き proxy 経由で動かない場合の一次切り分けは「101 が返っているか」。

#関連

HTTP/2 環境では Upgrade の代わりに CONNECT メソッドの拡張(RFC 8441)でWebSocketをストリームに載せる方式があり、HTTP/3向けにもExtended CONNECTを使うブートストラップ(RFC 9220)が定義されています。実際に利用できるかはクライアント、サーバー、中間装置の対応によります。双方向ストリーミングが目的なら gRPC のstreaming RPCも比較対象になります。