広告が嫌いですか? 行く 広告なし 今日

WebSockets と SSE と長時間ポーリング — チャットアプリが溶ける前に正しいリアルタイムパターンを選んでください

更新日

Most developers default to WebSockets without thinking. This breakdown of WebSockets, Server-Sent Events, and long polling covers the real tradeoffs — latency, reconnect behavior, infra complexity, bidirectionality — so you can pick the right pattern and stop over-engineering your data feed.

WebSockets vs SSE vs Long Polling — Pick the Right Real-Time Pattern Before Your Chat App Melts 1

You built a dashboard. Users need live data updates. You reached for WebSockets — because that’s what everyone does. Now you’re debugging sticky sessions at 2 AM because your load balancer keeps sending clients to the wrong pod. The data you’re pushing? A single JSON number every 10 seconds. WebSockets were overkill.

This happens constantly. Developers treat WebSockets as the default real-time solution, when SSE or long polling would have shipped faster, scaled more easily, and cost less to operate. Here’s how to actually choose.

The Decision Table

Before the deep dive, here’s the full comparison. Use this to make a call, then read the sections below if you need the reasoning.

特徴長距離ポーリングServer-Sent EventsWebSockets
プロトコルHTTP/1.1HTTP/1.1+ (chunked)WS (HTTP upgrade)
方向Client pulls onlyServer → Client onlyFull bidirectional
ブラウザサポートAll browsersAll modern (no IE11)すべての現代ブラウザ
自動再接続Manual — you write the loopBuilt-in via EventSourceManual or via library
CDN / proxy friendlyはいMostly (watch timeouts)いいえ
Horizontal scalingStateless, trivialStateless, trivialRequires sticky sessions or pub/sub broker
Infra complexityLow — plain HTTPLow — plain HTTPHigh — stateful connections, LB config, broker
LatencyPoll interval + round tripNear real-timeNear real-time
最適な用途Low-frequency updates, legacy infraDashboards, feeds, notificationsChat, collaborative editing, gaming

Long Polling: The Unsexy Workhorse

Long polling is not “old” or “wrong” — it’s just HTTP. The client makes a request, the server holds it open until there’s something to say (or a timeout fires), the client gets the response and immediately fires the next request. Repeat.

It’s been around since before WebSockets existed and it still runs millions of production apps. The reason: it’s just HTTP. Your existing reverse proxy, CDN, load balancer, and monitoring all understand it without configuration. No sticky sessions, no protocol upgrades, no WS-aware infrastructure.

The downsides are real though. Every “update” is a full HTTP request cycle — headers, TCP overhead (unless HTTP/2), server-side request queuing. Under high concurrency this gets expensive on the server. And the latency floor is the round-trip time of each poll, not the latency of the event itself. If you’re polling every 5 seconds and an event fires at second 1, the user waits 4 more seconds.

When to use it: Legacy systems where adding a persistent connection layer isn’t worth the infra change. Low-frequency updates (once per minute or less). Environments where SSE gets killed by overzealous proxies. Polling that clients explicitly control — job status checks, async task results.

Server-Sent Events: The One Everyone Forgets

SSE is a standard (HTML Living Standard) that lets a server push a stream of text events over a single long-lived HTTP connection. The browser handles reconnection automatically. You don’t write a reconnect loop — the EventSource API just reconnects with the last event ID it saw, so you can resume a stream after a network blip.

The wire format is embarrassingly simple:

HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache

id: 1
event: price-update
data: {"symbol":"BTC","price":67420}

id: 2
event: price-update
data: {"symbol":"ETH","price":3521}

Each event is plain text with optional id, event, dataと、 retry fields, separated by blank lines. You can test SSE endpoints directly with cURL — use the cURLコマンドビルダー to construct the request with the right Accept header (text/event-stream) and stream the response.

On the client side:

const es = new EventSource('/api/events');

es.addEventListener('price-update', (e) => {
  const payload = JSON.parse(e.data);
  updateDashboard(payload);
});

es.onerror = () => {
  // EventSource will auto-reconnect. You don't need to do anything here
  // unless you want to log or update UI state.
};

SSE has one real limitation: it’s server-to-client only. The client can’t push data back over the same connection — any client-initiated actions go over normal HTTP requests. For most dashboard, notification, and feed use cases, that’s fine. You weren’t using the client-to-server direction anyway.

The other gotcha: some proxies buffer the response body and only forward it after the connection closes, which breaks streaming entirely. NGINX needs proxy_buffering off. Cloudflare’s free tier buffers SSE. If your infra has a buffering proxy in the middle and you can’t configure it, SSE won’t work and you’ll need WebSockets or long polling.

When to use it: Live dashboards, notification feeds, activity logs, AI response streaming (this is exactly what ChatGPT’s streaming API uses), stock tickers, deploy/build log tailing. Anywhere the server has data to push and the client doesn’t need to push back.

WebSockets: When They’re Actually Worth It

WebSockets give you a full-duplex, persistent connection over a single TCP socket. After the HTTP upgrade handshake, both sides can send framed messages at any time. The latency is as low as you can get over a network — no new request per message, no headers on each frame.

The tradeoff is that they’re stateful. The server needs to track every open connection. This immediately creates problems at scale: load balancers route by connection (not request), so you need sticky sessions or a pub/sub broker (Redis, etc.) to fan out messages across multiple server instances. Your CDN can’t cache WebSocket traffic. Your reverse proxy needs explicit configuration for the upgrade. Your monitoring needs to understand WS frames if you want payload-level visibility.

None of this is insurmountable, but it’s real work. When you’re starting a project and evaluating options, “it’s just HTTP” vs. “stateful WS connections with a Redis pub/sub layer” is a meaningful difference in complexity.

WebSocket payloads are typically JSON. If you’re debugging a WS integration and need to inspect or format the messages, the JSONフォーマッタ makes it easier to read minified payloads from your dev tools network tab.

使用する場合: Chat applications (genuinely bidirectional — every message is client-initiated). Collaborative editing (Figma, Google Docs — multiple clients writing to shared state). Multiplayer games (high-frequency, low-latency state sync). Financial trading terminals (sub-100ms latency on order books). Basically: anything where the client needs to push data frequently, not just receive it.

The Scaling Gotcha Nobody Mentions Until It’s Too Late

Long polling and SSE are stateless at the HTTP layer. Each request can land on any server instance. Your horizontal scaling story is boring and that’s a feature: add more instances, done. Connection state lives only for the duration of the open HTTP connection, and load balancers route freely.

WebSockets are stateful. Client A’s connection lives on Server 1. When you want to push a message to Client A from Server 2 (because the event originated there), Server 2 has to know about that connection. The standard solution is a pub/sub broker — Redis pub/sub, Kafka, or managed services like Pusher or Ably that abstract all of this away. You either build that layer yourself or you pay for it.

Pusher charges $49/month for 500 concurrent connections. Ably is similarly priced. If your “real-time feature” is a notification badge that updates when someone likes your post, that’s a lot of money for SSE you could host for free alongside your existing API.

HTTP/2 Changes the Long Polling Math

Classic HTTP/1.1 long polling is one request per connection per in-flight hold. Under HTTP/2, multiplexing means multiple streams over one TCP connection. The overhead is lower. This isn’t a reason to pick long polling over SSE, but it means the “HTTP overhead is expensive” criticism applies less in 2024 than it did in 2012 when these comparisons were first written.

If you’re on HTTP/2 end-to-end (client to server, no intervening proxy that downgrades), long polling scales better than most older benchmarks suggest.

Quick Decision Guide

Stop reading here if your use case fits one of these cleanly:

  • Update frequency < 1/minute, legacy infra, simple polling semantics: Long polling. Don’t complicate it.
  • Server pushes data, client mostly reads, standard HTTP infra: SSE. This covers 80% of “real-time” features.
  • Client sends data frequently, latency < 100ms matters, truly bidirectional: WebSockets. Now it’s worth the complexity.
  • You’re using AI streaming (OpenAI, Anthropic, etc.): SSE — that’s literally what their streaming APIs use.
  • Multiplayer game or collaborative editor: WebSockets, or a purpose-built framework (Liveblocks, PartyKit, etc.).

The default answer isn’t WebSockets. It’s SSE — until you have a specific reason it isn’t enough. The number of production apps that actually need full bidirectional real-time is much smaller than the number that をリポジトリにラインエンドポリシーとして設定してください。そして WebSockets. That gap is engineering debt accumulated at 2 AM in front of a load balancer config.

広告なしで楽しみたいですか? 今すぐ広告なしで

拡張機能をインストールする

お気に入りのブラウザにIOツールを追加して、すぐにアクセスし、検索を高速化します。

に追加 Chrome拡張機能 に追加 エッジ拡張 に追加 Firefox 拡張機能 に追加 Opera 拡張機能

スコアボードが到着しました!

スコアボード ゲームを追跡する楽しい方法です。すべてのデータはブラウザに保存されます。さらに多くの機能がまもなく登場します!

ニュースコーナー 技術ハイライト付き

参加する

価値ある無料ツールの提供を継続するためにご協力ください

コーヒーを買って