Skip to main content
このページで解説する MercureBroadcasterlaravel/framework の PR #61474(2026年9月10日マージ)で追加された機能です。執筆時点ではタグ付きリリースにまだ含まれておらず、公式ドキュメントにも記載がありません。ソースコードとdocblockを元にした先行解説なので、実際に使う場合はlaravel/frameworkの変更履歴を確認してください。

概要

Laravelのブロードキャストは長らく Reverb・Pusher・Ably という「WebSocketネイティブ」なドライバーを提供してきましたが、config/broadcasting.phpdriverに新しくmercureが加わりました。 Mercure は Server-Sent Events (SSE) をベースにしたリアルタイム通信プロトコルです。WebSocketのような双方向コネクションではなく、HTTP/1.1・HTTP/2上のロングポーリングに近い仕組みで動作するため、以下のような特徴があります。
  • 通常のHTTPインフラ(リバースプロキシ、CDN、ロードバランサー)を素通りできる
  • ブラウザのEventSource APIだけでクライアントを実装でき、専用クライアントライブラリなしで動く
  • FrankenPHP には組み込みのMercureハブが搭載されているため、追加のインフラなしで動かせる

driverの追加とサポートされる値

config/broadcasting.php の冒頭コメントに追記されたサポートドライバー一覧にmercureが加わりました。
接続設定のサンプルも用意されています。
urlを省略した場合、FrankenPHPの組み込みMercureハブ(mercure_publish()関数)へ自動フォールバックします。この判定はCreatesMercureDrivers::mercure()で行われています。
外部にMercureハブを立てている場合はurlに管理API(publish用)のURL、public_urlにブラウザから見えるURLを指定します。両者が異なるのは、Docker Composeなど内部ネットワーク経由でpublishし、公開URLだけをブラウザに晒す構成を想定しているためです。

トークン発行の仕組み:publish用とsubscribe用を分離

MercureはJWT (JSON Web Token) でアクセス制御を行うプロトコルです。CreatesMercureDriversトレイトは、publish(サーバー→ハブ)とsubscribe(ブラウザ→ハブ)で 別々のトークンファクトリー を組み立てます。
  • secret / publish_secret / subscribe_secret を個別に指定でき、省略時はsecretにフォールバック
  • algorithm / publish_algorithm / subscribe_algorithm も同様に個別指定可能(デフォルトHS256
  • HS256は32バイト、HS384は48バイト、HS512は64バイト以上のシークレット長が必須で、満たさない場合はInvalidArgumentExceptionが投げられます
publishトークンはGrant::ACTION_PUBLISHで全トピック(*)への発行権限を持ち、CachingTokenProviderでメモリキャッシュされます。毎回のブロードキャストごとにJWTを再生成しないための最適化です。

チャンネル認可とCookieベースの購読モデル

WebSocketドライバーとの最大の違いは、購読側の認可が 単一のCookie で行われる点です。MercureBroadcaster::auth()channel_names配列(1リクエストあたり最大100件)を受け取り、チャンネルごとに認可を判定してから、まとめて1つの認可Cookieを発行します。
ポイントは「1チャンネルの認可が失敗しても、レスポンス全体を失敗させない」設計です。拒否されたチャンネルはdenied: trueとしてレスポンスに含まれるだけで、他の許可されたチャンネルの購読は継続します。これは、Mercureが1つのEventSourceで複数トピックを多重化するため、途中でチャンネルを追加・削除しても既存の接続を切断せずに済むようにするためです。 Presenceチャンネルは通常のprivate/private-encryptedチャンネルと異なるGrantを発行し、購読URLパターン(MercureのSubscription API)に対するsubscribe権限として組み込まれます。

エンドツーエンド暗号化チャンネル

private-encrypted-で始まるチャンネル名は、Mercureハブ自体にもペイロードを見せない「エンドツーエンド暗号化(E2EE)」チャンネルとして扱われます。これは既存のPusher/Reverbドライバーにはない機能です。 encryption_keyをbase64エンコードされた32バイトの鍵として設定するとChannelEncrypterが有効になり、broadcast()実行時にイベント名・ペイロード・ソケットIDをまとめてJWE(JSON Web Encryption)として暗号化します。ハブが中継するのは暗号化済みのバイト列だけです。
購読側はauth()レスポンスに含まれるjwk(JSON Web Key)フィールドを使ってブラウザ側で復号します。鍵交換はMercure仕様が推奨する「認可レスポンスに載せてアウトオブバンドで渡す」方式で、ハブ自体は鍵を一切知りません。
Presenceチャンネルは暗号化の対象外です。メンバー一覧がMercureのSubscription APIを経由してハブに見える仕様のため、E2EEとは両立しません。

Whisper(クライアント間の直接メッセージ)

client_eventsが有効な場合(デフォルトtrue)、各ガード付きチャンネルには専用の「whisperトピック」が割り当てられ、購読者はそのトピックに対してのみpublish権限を持ちます。
チャンネル本体のトピックはサーバー専用のまま保たれるため、クライアントが偽装したサーバーイベントを発行することはできません。Pusherの「クライアントイベント」機能に近い設計ですが、トピックを分離することでより明確に権限を分けています。

トピックの命名規則

Mercureはハブを複数のアプリケーションで共有できる設計になっており、トピックの衝突を避けるためにtopic_prefixという名前空間を導入しています。デフォルト値は次のとおりです。
.altはDNS上で予約された未解決ドメイン(RFC 9476)なので、実在のドメインと衝突する心配がありません。チャンネル名はRFC 3986に従ってURLエンコードされ、単一のパスセグメントに収められます。

Cookieドメインとエラーメッセージ

Mercureハブがアプリケーションと異なるサブドメインで動く構成もよくあるため(例:mercure.example.comapp.example.com)、認可Cookieのドメイン解決に失敗すると、原因を特定しやすい例外メッセージが投げられます。
__Secure-__Host-プレフィックス付きのCookie名を使う場合、public_urlがHTTPSであることも起動時にチェックされます。

Reverb・Pusher・Ablyとの使い分け

MercureはWebSocketの常時双方向接続が不要なユースケース(通知、進捗表示、チャットのようにサーバー→クライアントの一方向配信が中心の用途)に向いています。FrankenPHPを使っている場合は追加インフラなしで動作するのも利点です。

関連ページ

最終更新日 2026年9月13日