Überblick
Die Broadcasting-Schicht von Laravel liefert seit Langem Reverb, Pusher und Ably als „WebSocket-native” Treiber. Neu hinzugekommen ist der Treiberwertmercure in config/broadcasting.php.
Mercure ist ein Echtzeitprotokoll, das auf Server-Sent Events (SSE) statt auf einer bidirektionalen WebSocket-Verbindung aufsetzt. Es verhält sich eher wie Long Polling über HTTP/1.1 oder HTTP/2, woraus sich einige besondere Eigenschaften ergeben:
- Es passiert reguläre HTTP-Infrastruktur (Reverse Proxies, CDNs, Load Balancer) transparent.
- Clients lassen sich allein mit der
EventSource-API des Browsers implementieren — eine dedizierte Client-Bibliothek ist nicht erforderlich. - FrankenPHP bringt einen eingebauten Mercure-Hub mit, sodass zum Ausprobieren keine zusätzliche Infrastruktur nötig ist.
Der neue driver-Wert
Der Kommentar mit den unterstützten Treibern am Anfang von config/broadcasting.php listet nun auch mercure auf:
url weggelassen, fällt der Treiber auf den in FrankenPHP eingebauten Mercure-Hub (die Funktion mercure_publish()) zurück. Diese Entscheidung fällt in CreatesMercureDrivers::mercure():
url auf die Management-API zum Publizieren zeigen, während public_url die URL ist, mit der sich der Browser verbindet. Diese Trennung unterstützt Setups, in denen das Publizieren über ein internes Netzwerk (etwa Docker Compose) läuft, während nur die öffentliche URL für den Browser sichtbar ist.
Getrennte Publish- und Subscribe-Tokens
Mercure ist ein JWT-basiertes Protokoll zur Zugriffskontrolle. Der TraitCreatesMercureDrivers baut getrennte Token-Factories für das Publizieren (Server → Hub) und das Abonnieren (Browser → Hub) auf:
secret,publish_secretundsubscribe_secretlassen sich jeweils unabhängig konfigurieren und fallen aufsecretzurück.- Analog können
algorithm,publish_algorithmundsubscribe_algorithmseitengetrennt gesetzt werden (Standard:HS256). - HS256 verlangt ein Secret von mindestens 32 Byte, HS384 mindestens 48 und HS512 mindestens 64; andernfalls wird eine
InvalidArgumentExceptiongeworfen.
Grant::ACTION_PUBLISH für alle Topics (*) und wird vom CachingTokenProvider memoisiert, sodass nicht bei jedem einzelnen Broadcast-Aufruf ein neues JWT erzeugt wird.
Kanalautorisierung über ein einziges Cookie
Der größte architektonische Unterschied zu den WebSocket-Treibern besteht darin, dass die Autorisierung der Abonnenten über ein einziges Cookie abgewickelt wird.MercureBroadcaster::auth() nimmt ein channel_names-Array entgegen (begrenzt auf 100 Einträge pro Anfrage), prüft die Autorisierung pro Kanal und stellt anschließend ein einzelnes Autorisierungs-Cookie aus, das alle Kanäle abdeckt:
denied: true markiert, während die übrigen autorisierten Kanäle weiterhin funktionieren. Das ist wichtig, weil Mercure viele Topics über eine einzige EventSource-Verbindung multiplext, sodass Kanäle mitten in einer Sitzung hinzugefügt oder entfernt werden können, ohne die Verbindung abbauen zu müssen.
Presence-Kanäle erhalten eine andere Grant-Form als reguläre private- oder private-encrypted-Kanäle; sie zielen auf das Subscribe-URL-Muster ab, das die Subscription API von Mercure verwendet.
Ende-zu-Ende-verschlüsselte Kanäle
Kanäle mit dem Präfixprivate-encrypted- werden als Ende-zu-Ende-verschlüsselt (E2EE) behandelt — das bedeutet, dass selbst der Mercure-Hub die Nutzdaten nie zu sehen bekommt. Diese Fähigkeit bieten die bestehenden Pusher- und Reverb-Treiber nicht.
Wird ein base64-kodierter, 32 Byte langer encryption_key gesetzt, aktiviert das den ChannelEncrypter. Dieser verpackt Eventnamen, Nutzdaten und Socket-ID zu einer JWE (JSON Web Encryption), bevor broadcast() sie versendet. Der Hub leitet ausschließlich verschlüsselte Bytes weiter.
jwk-Feldes (JSON Web Key), das die auth()-Antwort zurückliefert — ein von der Mercure-Spezifikation empfohlener Out-of-Band-Schlüsselaustausch, bei dem der Hub den Schlüssel niemals erfährt.
Presence-Kanäle können nicht verschlüsselt werden, da ihre Mitgliederliste konstruktionsbedingt über die Subscription-API des Hubs läuft — und das ist mit E2EE nicht vereinbar.
Whispers (direkte Nachrichten zwischen Clients)
Istclient_events aktiviert (Standardwert), erhält jeder geschützte Kanal ein eigenes „Whisper-Topic”, auf dem Abonnenten publizieren dürfen:
Topic-Benennung
Da Mercure-Hubs häufig von mehreren Anwendungen gemeinsam genutzt werden, werden Topics unter einemtopic_prefix in einem Namensraum abgelegt, um Kollisionen zu vermeiden. Der Standardwert lautet:
.alt ist ein reservierter, nicht auflösbarer DNS-Suffix (RFC 9476) und kann daher niemals mit einer realen Domain kollidieren. Kanalnamen werden gemäß RFC 3986 URL-kodiert in ein einzelnes Pfadsegment gepackt:
Cookie-Domain und Fehlermeldungen
Da ein Mercure-Hub häufig auf einer anderen Subdomain läuft als die Anwendung (etwamercure.example.com gegenüber app.example.com), wirft der Treiber bei einer misslungenen Auflösung einer gemeinsamen Cookie-Domain eine aussagekräftige Exception:
__Secure- oder __Host-, prüft der Treiber beim Booten außerdem, ob public_url per HTTPS ausgeliefert wird:
Wahl zwischen Reverb, Pusher/Ably und Mercure
Mercure eignet sich gut, wenn Sie keine dauerhafte bidirektionale Verbindung benötigen — für Benachrichtigungen, Fortschrittsanzeigen oder chatartige Anwendungsfälle, die vorwiegend vom Server zum Client fließen. Wenn Sie bereits FrankenPHP einsetzen, funktioniert Mercure ganz ohne zusätzliche Infrastruktur.
Verwandte Seiten
- Grundlagen des Broadcastings
- Laravel Cloud (FrankenPHP-basierte Laufzeitumgebung)