概要
Laravelのブロードキャストは長らく Reverb・Pusher・Ably という「WebSocketネイティブ」なドライバーを提供してきましたが、config/broadcasting.php のdriverに新しくmercureが加わりました。
Mercure は Server-Sent Events (SSE) をベースにしたリアルタイム通信プロトコルです。WebSocketのような双方向コネクションではなく、HTTP/1.1・HTTP/2上のロングポーリングに近い仕組みで動作するため、以下のような特徴があります。
- 通常のHTTPインフラ(リバースプロキシ、CDN、ロードバランサー)を素通りできる
- ブラウザの
EventSourceAPIだけでクライアントを実装でき、専用クライアントライブラリなしで動く - FrankenPHP には組み込みのMercureハブが搭載されているため、追加のインフラなしで動かせる
driverの追加とサポートされる値
config/broadcasting.php の冒頭コメントに追記されたサポートドライバー一覧にmercureが加わりました。
urlを省略した場合、FrankenPHPの組み込みMercureハブ(mercure_publish()関数)へ自動フォールバックします。この判定はCreatesMercureDrivers::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が投げられます
Grant::ACTION_PUBLISHで全トピック(*)への発行権限を持ち、CachingTokenProviderでメモリキャッシュされます。毎回のブロードキャストごとにJWTを再生成しないための最適化です。
チャンネル認可とCookieベースの購読モデル
WebSocketドライバーとの最大の違いは、購読側の認可が 単一のCookie で行われる点です。MercureBroadcaster::auth()はchannel_names配列(1リクエストあたり最大100件)を受け取り、チャンネルごとに認可を判定してから、まとめて1つの認可Cookieを発行します。
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権限を持ちます。
トピックの命名規則
Mercureはハブを複数のアプリケーションで共有できる設計になっており、トピックの衝突を避けるためにtopic_prefixという名前空間を導入しています。デフォルト値は次のとおりです。
.altはDNS上で予約された未解決ドメイン(RFC 9476)なので、実在のドメインと衝突する心配がありません。チャンネル名はRFC 3986に従ってURLエンコードされ、単一のパスセグメントに収められます。
Cookieドメインとエラーメッセージ
Mercureハブがアプリケーションと異なるサブドメインで動く構成もよくあるため(例:mercure.example.com と app.example.com)、認可Cookieのドメイン解決に失敗すると、原因を特定しやすい例外メッセージが投げられます。
__Secure-や__Host-プレフィックス付きのCookie名を使う場合、public_urlがHTTPSであることも起動時にチェックされます。
Reverb・Pusher・Ablyとの使い分け
MercureはWebSocketの常時双方向接続が不要なユースケース(通知、進捗表示、チャットのようにサーバー→クライアントの一方向配信が中心の用途)に向いています。FrankenPHPを使っている場合は追加インフラなしで動作するのも利点です。
関連ページ
- ブロードキャストの基礎
- Laravel Cloud(FrankenPHPベースの実行環境)