WebSocket・SSE・ポーリングの使い分け - リアルタイム通信の選び方

WebSocket・SSE・ポーリングの使い分け - リアルタイム通信の選び方

作成日:
読了:10
更新日:

チャット、通知、株価、進捗バー——「サーバーの変化をすぐ画面に反映したい」とき、選択肢は大きく ポーリング・SSE・WebSocket の3つです。なんとなく WebSocket を選びがちですが、用途によっては SSE のほうが圧倒的に楽なこともあります。この記事では、MDN・RFC 6455 / 6202 を一次ソースに、3方式の違いと選び方を整理します。

3つの方式

ポーリング(short / long polling)

  • Short polling: クライアントが一定間隔でHTTPリクエストを繰り返す。データが無くてもサーバーは即レスポンスを返す。最も単純だが、更新が無いときも通信が走る
  • Long polling(RFC 6202): サーバーがイベント発生かタイムアウトまでレスポンスを保留する。クライアントは受信したらすぐ次のリクエストを送る。安全なタイムアウト値の目安は30秒前後

SSE(Server-Sent Events / EventSource)

  • サーバー → クライアントの単方向ストリーム
  • HTTP の上で動き、Content-Type: text/event-stream を返すだけ
  • 自動再接続が標準装備Last-Event-ID で中断点から再送できる
  • テキスト(UTF-8)のみ。バイナリは Base64 等にする必要がある

WebSocket

  • 全二重(双方向同時)ws:// / wss://
  • HTTP からの Upgrade ハンドシェイク101 Switching Protocols)で専用プロトコルに切り替える
  • バイナリフレーム対応。チャット・ゲーム・協調編集などに向く

比較表

項目Short PollingLong PollingSSEWebSocket
通信方向単方向単方向単方向(サーバー→)双方向
プロトコルHTTPHTTPHTTP(text/event-stream)ws / wss
自動再接続自作自作標準装備自作
バイナリ不可(テキストのみ)
オーバーヘッド毎回大大(頻度は低)接続時のみ接続時のみ
実装の手軽さ最も簡単やや複雑シンプルサーバー側がやや複雑
HTTP/2の恩恵多重化多重化接続数上限が解消無関係(独立プロトコル)

コード例

Long polling(クライアント)

long polling
async function longPoll() {
  try {
    const res = await fetch("/events");
    handleData(await res.json());
  } catch {
    await new Promise((r) => setTimeout(r, 1000)); // 失敗時は少し待つ
  }
  longPoll(); // 常に次を投げる
}
longPoll();

サーバーはデータが来るまでレスポンスを保留し、タイムアウト(例: 30秒)で空を返します。

SSE(クライアント)

EventSource
const es = new EventSource("/events");
 
es.onmessage = (e) => console.log(e.data);
 
// 名前付きイベント
es.addEventListener("price", (e) => console.log(JSON.parse(e.data)));
 
es.onerror = (err) => console.error("SSE error", err);
// 不要になったら es.close();

SSE(サーバーの応答形式)

text/event-stream
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
 
: コメント(ハートビートにも使える)
 
id: 42
event: price
data: {"symbol":"USDJPY","price":150.5}
 
retry: 3000
data: 3秒後に再接続してね

各メッセージは空行で区切るのがポイント。id: を付けると、再接続時に Last-Event-ID ヘッダで送られます。

WebSocket(クライアント)

WebSocket
const ws = new WebSocket("wss://example.com/ws");
 
ws.addEventListener("open", () => ws.send(JSON.stringify({ type: "hello" })));
ws.addEventListener("message", (e) => console.log(JSON.parse(e.data)));
ws.addEventListener("close", (e) => console.log("closed", e.code, e.reason));

WebSocket は最初だけ HTTP でハンドシェイクします。

Upgrade ハンドシェイク
GET /ws HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
 
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

用途別の選び方

  • 単純な定期更新(管理画面のダッシュボードなど)→ ポーリング。実装が最も単純
  • サーバーからの一方的な配信(通知・フィード・株価・ログ)→ SSE。HTTP の上で動き、再接続も標準。インフラ変更が要らない
  • 双方向のリアルタイム(チャット・ゲーム・協調編集)→ WebSocket。全二重が要る場面
  • 「WebSocket が要りそうで実は SSE で足りる」ケースは多い。受信は SSE・送信は通常の fetch(POST)に分ければ、双方向を疑似的にまかなえてインフラが軽い

NOTE

迷ったらまず「本当に双方向が必要か」を問い直してください。サーバー→クライアントの配信が主目的なら、SSE のほうが実装・運用ともに軽く済むことが多いです。

よくある落とし穴

SSE

  • HTTP/1.1 の接続数上限: ブラウザはドメインあたり最大6接続。複数タブで同一ドメインに繋ぐと枯渇する。HTTP/2 なら多重化され実質解消
  • プロキシのバッファリング: Nginx は既定でレスポンスをバッファするため、イベントがまとめて届く(リアルタイムでなくなる)。proxy_buffering offX-Accel-Buffering: no で対処

WebSocket

  • 認証でカスタムヘッダが使えない: ブラウザの WebSocket API は Authorization 等のヘッダを付けられない。代替は (1) Cookie(同一ドメインなら自動送信。Cookie と SameSite参照)、(2) クエリにトークン(URL に残るリスク)、(3) 接続後の最初のメッセージで認証
  • スケールと sticky session: 長時間ステートフルな接続のため、ロードバランサで sticky が要りがち。状態は Redis 等の外部ストアへ逃がす
  • ハートビート: 経路上のプロキシが無通信の接続を切ることがある。Ping/Pong で生存維持

Long polling

  • ヘッダのオーバーヘッド: 毎回フルの HTTP ヘッダを往復するため、小さなペイロードでは割高

新顔: WebTransport

WebTransport(HTTP/3 / QUIC ベース)は、双方向・単方向ストリームとデータグラム(非信頼配信)を持ち、多重化で Head-of-Line ブロッキングを避けられます。MDN は「多くの用途で WebSocket を置き換えうる候補」と位置づけていますが、利用可能になったのは新しいブラウザ(2026年前後〜)で、旧環境は非対応です。低遅延ゲームなど先端用途で選択肢に入ります(普及はこれから)。

まとめ

  • ポーリング=単純な定期更新、SSE=サーバーからの単方向配信、WebSocket=双方向リアルタイム
  • SSE は HTTP の上で動き、自動再接続が標準。単方向で足りるならまず SSE
  • WebSocket は強力だが、認証(カスタムヘッダ不可)・スケール(sticky/外部ストア)・ハートビートに注意
  • SSE の HTTP/1.1 接続数上限プロキシのバッファリングは定番のハマりどころ
  • 先端用途には WebTransport(HTTP/3)も選択肢に

「双方向が本当に要るか」を最初に決めるだけで、選択はぐっと簡単になります。多くの配信系は SSE で十分で、双方向が必須のときだけ WebSocket、が実務的な指針です。

参考リンク

Astro 6 の新機能 - 開発と本番の差を埋める新 astro dev、Fonts API、移行の注意点

Astro 6 の新機能 - 開発と本番の差を埋める新 astro dev、Fonts API、移行の注意点

9

Astro 6 の新機能と移行ポイントを公式リリースノートをもとに整理します。Vite の Environment API を使い本番ランタイムを開発時に走らせる新しい astro dev、組み込み Fonts API、Live Content Collections と CSP の安定化、実験的な Rust コンパイラ。さらに 6.1 の変更と、Node 22.12 必須・Vite 7・レガシー Content Collections 廃止・ViewTransitions から ClientRouter への置き換えといった 5.x からの破壊的変更まで、Astro 6.0/6.1 を一次ソースでまとめます。

CloudflareがVoidZeroを買収 - Vite / Vitest / Rolldown / Oxc エコシステムの今後とVite+

CloudflareがVoidZeroを買収 - Vite / Vitest / Rolldown / Oxc エコシステムの今後とVite+

10

2026年6月4日、CloudflareがVite・Vitest・Rolldown・Oxcを擁するVoidZeroを買収しました。VoidZeroとは何か、Evan Youが統一を進めてきたツールチェーン、当初有償から完全MITオープンソースへ転換したVite+、買収の狙い(AI-nativeなWebとCloudflareへの統合)、そして「ベンダー中立性は保たれるのか」という開発者の懸念まで、Cloudflare・VoidZero・Viteの公式発表を一次ソースに整理します。