Server-Sent Events
さーばーせんといべんつ
HTTP接続を開いたままサーバーから一方向にイベントを流し続けるシンプルなプッシュ技術。
概要
Server-Sent Events(SSE)は、サーバーからブラウザへ一方向にデータを流し続けるための標準技術です。通常の HTTP は「クライアントが尋ね、サーバーが1回答えたら終わり」ですが、SSE ではレスポンスを終わらせずに接続を開いたままにし、サーバー側で何かが起きるたびにその接続へイベントを書き足していきます。株価やスポーツの実況、通知、進捗表示のように「サーバー側の変化を即座に画面へ反映したい」場面で使われます。
特筆すべきはそのシンプルさです。特別なプロトコルへの切り替えは行わず、あくまで「終わらない HTTP レスポンス」として動くため、既存のサーバー・プロキシ・認証の仕組みがほぼそのまま使えます。ブラウザ側も EventSource という組み込み API を数行書くだけで受信でき、切断時の自動再接続まで標準で備えています。近年は LLM のチャット応答を一文字ずつ流すストリーミング表示の定番手段として、再び脚光を浴びています。
なぜ生まれたか
SSE 以前、「サーバー側の更新をブラウザに知らせる」には、クライアントが定期的に「何か変わった?」と問い合わせるポーリングしかありませんでした。間隔を短くすればサーバーの負荷とトラフィックが跳ね上がり、そのほとんどは「変化なし」という無駄な往復です。間隔を長くすれば今度は反映が遅れます。応答を保留して引き延ばすロングポーリングなどの工夫もありましたが、いずれも HTTP の「尋ねて答える」を無理に曲げた変則技で、実装が複雑になりがちでした。
そこで HTML5 の標準化の中で、「多くのユースケースは双方向通信ではなく、サーバーからの一方向の通知で足りる」という観察に基づき、HTTP の枠内で最小限のプッシュを実現する仕組みとして SSE(EventSource API と text/event-stream 形式)が定義されました。全二重の WebSocket が「新しい通信路を開く」アプローチだとすれば、SSE は「今ある HTTP を終わらせない」アプローチです。
詳細
仕組み — 終わらないレスポンスとtext/event-stream
クライアントが new EventSource(url) で接続すると、通常の GET リクエストが飛び、サーバーは Content-Type: text/event-stream でレスポンスを返し始めます。ただし本文を完結させず、イベントが発生するたびに data: 本文 という行を空行区切りで書き足していきます。形式はただのテキストで、event: でイベント名、id: で通し番号、retry: で再接続間隔も指定できます。
自動再接続は SSE の隠れた主役です。接続が切れるとブラウザは自動的に再接続し、その際に最後に受け取ったイベントの id を Last-Event-ID ヘッダで送ります。サーバーがこれを見て未送信分から再開すれば、取りこぼしのない配信を比較的簡単に実現できます。WebSocket では再接続も欠落補償もすべて自前実装になるのと対照的です。
WebSocketとの使い分け
選択の軸は「双方向が本当に必要か」です。チャットの送信、共同編集、オンラインゲームのように、クライアントからも高頻度でデータを送るなら WebSocket が適します。一方、通知・フィード更新・進捗・ログ表示のように、流れが「サーバーからクライアントへ」の一方向なら SSE で十分で、HTTP のままなので認証や圧縮、リバースプロキシや CDN との相性という運用面で楽ができます。なお SSE はテキスト専用で、バイナリを流したい場合も WebSocket(またはより新しい WebTransport)の出番です。クライアントから送りたいことがたまにある程度なら、「受信は SSE、送信は普通の POST」という組み合わせも実用的な定番です。
LLMストリーミングでの再脚光
長らく地味な存在だった SSE を一躍表舞台に戻したのが、LLM のストリーミング応答です。生成 AI の応答は完成まで数秒〜数十秒かかるため、生成されたそばからトークンを画面に流し込む体験が事実上の標準になりました。この配信手段として OpenAI や Anthropic の API が採用したのが SSE です。「サーバーからの一方向・テキスト・順序保証・切断への強さ」という SSE の性質が、トークン列の逐次配信という要件にぴたりと一致したためです。
落とし穴 — HTTP/1.1の接続数制限
SSE 最大の古典的な罠が、ブラウザの同時接続数制限です。HTTP/1.1 ではブラウザは同一ドメインに約6本しか接続を張れず、SSE は1本を占有し続けるため、タブを複数開くとあっという間に上限に達し、同じサイトへの他のリクエストまで詰まってしまいます。この問題は HTTP/2 でほぼ解消されました。多重化により SSE のストリームを何本開いても TCP 接続は1本で済むからです。SSE を本番投入するなら HTTP/2 以降を前提にするのが現在の定石です。このほか、経路上のプロキシやロードバランサがレスポンスをバッファリングしてイベントが届かなくなる問題も定番で、バッファリング無効化の設定や、無通信での切断を防ぐ定期的なコメント行(キープアライブ)の送信が実務上のお約束になっています。