LASSIC Media らしくメディア

2026.07.10 らしくコラム

SSEでストリーミング配信を実装、外注で進める

LASSIC IT事業部|元請(プライムベンダー)としてシステム保守・運用を受託

ストリーミングのイメージ

この記事のポイント

  • SSEはtext/event-stream形式でサーバーからブラウザへ一方向にデータを送る仕組みで、ブラウザ側はEventSourceインターフェースで受信します。
  • 生成AIのAPIでは、応答をトークン単位でSSE形式で配信する例があり、Anthropic Messages APIも公式ドキュメントでこの方式を採用すると説明しています。
  • 実装で見落としやすいのはコードそのものより、プロキシのバッファリングやロードバランサーのタイムアウト設定です。

SSE(Server-Sent Events)とは、サーバーからブラウザへの一方向プッシュ配信

リアルタイム配信のイメージ

SSE(Server-Sent Events)とは、サーバーが任意のタイミングでブラウザへデータを送り続けられる仕組みです*1。ブラウザ側はEventSourceというインターフェースを使い、サーバーへの接続、イベントやデータの受信、エラーの処理、接続の終了までをひとまとめに扱えます*1。生成AIの応答をトークン単位で逐次表示したり、処理の進捗や株価・スコアといった値の更新を配信したりする用途に向いています。

図
図:SSEはサーバーからブラウザへの一方向配信で、切断時はLast-Event-IDを付けて再接続します

SSEの通信はtext/event-stream形式のレスポンスとして送られ、内容はUTF-8で表現するとWHATWG HTML仕様に定められています*4。WebSocketとは異なり、SSEのデータ配信はサーバーからブラウザへの一方向に限られる点が特徴です*2。ブラウザからサーバーへ都度リクエストを送らずに済むため、通知や更新の配信だけを目的とする要件では、実装の見通しを立てやすくなります。

EventSourceの動作——text/event-streamの受信・自動再接続・Last-Event-ID

EventSourceオブジェクトはURLを渡して生成すると、readyStateプロパティで接続状態を管理します。値はCONNECTING(0)、OPEN(1)、CLOSED(2)の3種類です*2。openイベントで接続確立を検知し、messageイベントでデータを受け取り、errorイベントで接続失敗を検知します*2。closeメソッドを呼び出すと接続を終了し、readyStateはCLOSEDに変わります*2

メッセージの構成——event・data・id・retryの4フィールド

サーバーが送るイベントストリームは、event・data・id・retryという4種類のフィールドで構成されます*3*4。eventフィールドを指定すると名前付きイベントとして扱われ、省略した場合はmessageイベントとして届きます*3。dataフィールドが本体で、複数行に分けて送ると改行で連結されます*3。行頭がコロン(:)の行はコメントとして無視される仕様のため、接続を維持するための送信(キープアライブ)にも使われます*3

自動再接続とLast-Event-ID——途中から配信を再開する仕組み

接続が切れた場合、ブラウザは既定の挙動として自動的に再接続を試みます*3。再接続前の待機時間はretryフィールドでミリ秒単位を指定でき、サーバー側から調整できます*3。WHATWG仕様では、再接続に失敗した場合にブラウザが指数的なバックオフを挟むことも想定されています*4。再接続時にはLast-Event-IDというHTTPヘッダーが送られ、直前に受信したidフィールドの値をサーバーへ伝えます*3*4。サーバー側はこの値を見て、途中から配信を再開する処理を組めます。

WebSocketとの使い分け——双方向が不要ならSSEが軽量な選択肢

WebSocketは、ブラウザとサーバーの双方が任意のタイミングでデータを送れる双方向の通信方式です。一方のSSEは、データの流れがサーバーからブラウザへの一方向に限られる分、実装する内容も絞られます*2。チャット機能のようにブラウザから即時に細かいメッセージを送る要件では双方向のやり取りが前提になりますが、生成AIの応答表示や通知配信のように「サーバーから届く内容を受け取るだけ」の要件であれば、SSEの一方向という制約はそのまま設計の利点になります。

EventSourceは通常のHTTP接続としてサーバーに到達するため、既存のHTTPインフラや認証の仕組みと組み合わせやすい面があります。接続はブラウザの標準機能だけで開始でき、Web Workerの中でも利用できます*1。双方向のやり取りが必要になった時点でWebSocketへの切り替えを検討する、という順序で進める判断もできます。

両者の違いを整理すると次の通りです。

項目 SSE WebSocket
通信方向 サーバー→ブラウザの一方向*2 双方向
接続の基盤 通常のHTTP接続*1 専用プロトコルへアップグレード
データ形式 text/event-stream(UTF-8のテキスト)*4 テキスト・バイナリの両方
再接続 ブラウザが自動で再試行し、Last-Event-IDで再開できる*3*4 再接続の実装をアプリ側で用意する
ブラウザAPI EventSource*1 WebSocket

SSEが向く典型ユースケース——生成AIのトークン表示・進捗通知・リアルタイム更新

生成AIのAPIを使うチャット機能では、回答をトークン単位で逐次届ける配信方式にSSEが使われる例があります。Anthropic Messages APIのドキュメントでは、リクエストでstreamをtrueに設定すると、応答をSSEで段階的に配信すると説明されています*7。テキストの断片だけでなく、ツール利用や思考過程の途中経過もイベントとして順に届く構成です*7。ブラウザ側は届いたdataフィールドを都度画面へ反映すれば、文章が生成されていく様子をそのまま表示できます。

ほかにも、バッチ処理や大きなファイルの変換処理のように、完了までの時間が読みにくい処理の進捗を通知する用途にも向いています。処理が10%進んだ、50%進んだといった状態をイベントとして送れば、ブラウザ側は待機画面をポーリングせずに更新できます。株価やスコア、在庫数のように値が変わるたびに反映したい表示も、サーバー側が変化を検知して送るだけで済むため、SSEに向く用途です。

いずれの用途も、ブラウザからサーバーへ細かい指示を送り返す必要がない点が共通しています。配信中にブラウザ側から条件を変えたい、双方向に会話を続けたいという要件が出てきた場合は、SSEの一方向という前提とは合わなくなるため、設計の初期段階で見極めておくことが大切です。

実装で外せない実務ポイント——プロキシのバッファリング対策とタイムアウト

Webアプリのイメージ

SSEの実装でつまずきやすいのは、EventSource側のコードではなく、サーバーとブラウザの間に入るプロキシやロードバランサーの設定です。多くのリバースプロキシは応答をいったんバッファに溜めてから送り出す動作をするため、サーバーが逐次送ったデータがブラウザに届くまで滞留してしまう場合があります。

nginxを経由する構成では、レスポンスヘッダーにX-Accel-Buffering: noを付けることで、そのレスポンスだけバッファリングを無効にできます*5。nginx側のproxy_bufferingディレクティブでも同様の制御ができ、既定値はonです*5。SSEのエンドポイントに対しては、アプリケーション側でこのヘッダーを付けるか、該当のlocationでproxy_buffering offを設定するかのいずれかを選ぶことになります*5

タイムアウトの設計も見落としやすい点です。SSEは接続を長い時間開いたままにする通信のため、ロードバランサーの接続維持時間が短いと、データが届く前に切断されてしまいます。AWSのApplication Load Balancerでは、idle_timeout.timeout_secondsという接続属性の既定値が60秒です*6。配信の間隔がこれより空く場合は、コメント行を使った定期的なキープアライブの送信や、タイムアウト値そのものの見直しが必要になります*3*6。こうしたインフラ側の設定は、アプリケーションのコードだけを見ていても気づきにくく、外部の目線でチェックする価値がある工程です。

ブラウザ側の制約として、HTTP/2を使わずにSSEを利用する場合、1つのブラウザが同一オリジンへ開けるHTTP接続の数は6本程度に留まるとMDNは説明しています*2。複数タブや複数のEventSourceを同時に開く画面では、この制約が接続の取りこぼしにつながる場合があるため、HTTP/2への対応状況もあわせて確認しておく必要があります*2

内製と外注の分かれ目——スケールと運用体制で判断する

SSEそのものの実装は、サーバー側でtext/event-stream形式のレスポンスを返すコードと、ブラウザ側でEventSourceを生成するコードだけであれば、規模の小さいものは自社で対応できる場合もあります。判断が分かれるのは、同時に開く接続数が増えてきた場合や、複数のサーバーで配信を分散させる構成、社内の複数システムをまたいで進捗を配信するような構成です。

接続を長く開いたままにする通信は、サーバー1台あたりで保持できる接続数やメモリの使い方に直結します。既存のWebアプリケーション基盤にSSEを追加する場合、同期処理を前提にした構成のままでは、接続数が増えるにつれて負荷の見立てが崩れやすくなります。プロキシやロードバランサーの設定変更まで含めて依頼できるかどうかが、委託先を選ぶ際の分かれ目です。

元請(プライムベンダー)として保守・運用まで見据えた体制で依頼する場合は、実装だけでなく、配信基盤の構成やタイムアウト設計、障害時の再接続動作の確認までを一括して依頼できるかを確認しておくと、後工程での手戻りを減らせます。既存システムのどこにSSEを組み込むかによって必要な工数は変わってくるため、現状の構成を診断したうえで内製・外注の切り分けを検討することが実務的です。

まとめ:SSE実装で押さえる3つの判断軸

本稿ではSSE(Server-Sent Events)の仕組みと実装の実務を、公式情報をもとに整理しました。要点は3つに集約できます。第一に、SSEはサーバーからブラウザへの一方向配信で、EventSourceが接続・受信・再接続を担う仕組みです*1*2。第二に、生成AIのトークン表示や進捗通知、値の更新配信のように、ブラウザから細かい指示を送り返す必要のない用途で選択の候補になります*7。第三に、実装そのものより、プロキシのバッファリング設定やロードバランサーのタイムアウトといったインフラ側の調整が導入を左右し、内製と外注の判断材料になります*5*6

LASSICに相談するメリット

LASSIC IT事業部は、元請(プライムベンダー)としてWebアプリケーションの開発・運用を受託しています。SSEエンドポイントの実装からプロキシ・ロードバランサーの設定調整、WebSocketとの使い分けの整理、配信基盤のスケール検討まで、一貫して対応する体制を整えています。既存システムへの影響を抑えながら導入を進めたい企業様は、現状の構成診断からご相談いただけます。

よくある質問

SSEを使うと、生成AIの回答をどのように逐次表示できますか。

Anthropic Messages APIのようにstreamをtrueにして呼び出すAPIでは、応答がSSE形式で段階的に届きます*7。ブラウザ側ではEventSourceのmessageイベントで受け取ったdataフィールドを都度画面へ追加していくと、文章が生成される様子をそのまま表示できます*3

SSEとWebSocketは、どちらを選べばよいですか。

ブラウザから細かい指示を都度送り返す必要がなければ、一方向の配信で済むSSEの方が実装内容を絞りやすくなります*2。チャット機能のようにブラウザからの送信も頻繁に発生する要件では、双方向のWebSocketが前提になります。

接続がすぐに切れてしまう場合、何を確認すればよいですか。

まずロードバランサーやプロキシの接続維持時間を確認します。AWSのApplication Load BalancerではIdle timeoutの既定値が60秒です*6。配信の間隔がこれより空く場合は、コメント行によるキープアライブの送信や、タイムアウト値の見直しが必要です*3*6

nginxを経由する構成でデータが届くまでに時間がかかるのはなぜですか。

nginxの既定動作では応答がバッファに溜められるため、SSEのように少しずつ送るデータが一括で届いてしまう場合があります*5。X-Accel-Buffering: noヘッダーの付与や、proxy_buffering offの設定で解消できます*5

SSEの実装を外部に委託する場合、何を確認すればよいですか。

エンドポイントの実装だけでなく、プロキシやロードバランサーのバッファリング・タイムアウト設定まで含めて依頼できるかを確認します。同時接続数が増えた場合のサーバー側の見立てや、WebSocketへの切り替えが必要になった場合の対応方針も、契約前にすり合わせておくと後工程の手戻りを減らせます。

著者:テレリモ総研編集部 鈴木 亮佑


ITアウトソーシング・システム開発のご相談はLASSICへ

元請(プライムベンダー)として、貴社の課題に合わせた体制構築・開発支援をご提案します。まずはお気軽にご相談ください。

無料相談はこちら

ご不明な点はお問い合わせフォームからもご連絡いただけます。

  1. *1 出典:MDN Web Docs「Server-sent events」(https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events
  2. *2 出典:MDN Web Docs「EventSource」(https://developer.mozilla.org/en-US/docs/Web/API/EventSource
  3. *3 出典:MDN Web Docs「Using server-sent events」(https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events
  4. *4 出典:WHATWG「HTML: Server-sent events」(https://html.spec.whatwg.org/multipage/server-sent-events.html
  5. *5 出典:nginx「Module ngx_http_proxy_module」(X-Accel-Buffering/proxy_buffering)(https://nginx.org/en/docs/http/ngx_http_proxy_module.html
  6. *6 出典:AWS「Application Load Balancers」(Elastic Load Balancing User Guide、idle_timeout.timeout_seconds)(https://docs.aws.amazon.com/elasticloadbalancing/latest/application/application-load-balancers.html
  7. *7 出典:Anthropic「Streaming Messages」(Claude Docs)(https://platform.claude.com/docs/en/build-with-claude/streaming


View