FrankenPHP comes with a built-in Mercure hub! Mercure allows you to push real-time events to all the connected devices: they will receive a JavaScript event instantly.
It’s a convenient alternative to WebSockets that is simple to use and is natively supported by all modern web browsers!

Mercure support is disabled by default.
Here is a minimal example of a Caddyfile enabling both FrankenPHP and the Mercure hub:
# The hostname to respond to
localhost
mercure {
# The secret key used to sign the JWT tokens for publishers
publisher_jwt !ChangeThisMercureHubJWTSecretKey!
# Allows anonymous subscribers (without JWT)
anonymous
}
root public/
php_server
Tip
The sample
Caddyfileprovided by the Docker images already includes a commented Mercure configuration with convenient environment variables to configure it.Uncomment the Mercure section in
/etc/frankenphp/Caddyfileto enable it.
By default, the Mercure hub is available on the /.well-known/mercure path of your FrankenPHP server.
To subscribe to updates, use the native EventSource JavaScript class:
<!-- public/index.html -->
<!doctype html>
<title>Mercure Example</title>
<script>
const eventSource = new EventSource("/.well-known/mercure?topic=my-topic");
eventSource.onmessage = function (event) {
console.log("New message:", event.data);
};
</script>
file_put_contents()To dispatch an update to connected subscribers, send an authenticated POST request to the Mercure hub with the topic and data parameters:
<?php
// public/publish.php
const JWT = 'eyJhbGciOiJIUzI1NiJ9.eyJtZXJjdXJlIjp7InB1Ymxpc2giOlsiKiJdfX0.PXwpfIGng6KObfZlcOXvcnWCJOWTFLtswGI5DZuWSK4';
$updateID = file_get_contents('https://localhost/.well-known/mercure', context: stream_context_create(['http' => [
'method' => 'POST',
'header' => "Content-type: application/x-www-form-urlencoded\r\nAuthorization: Bearer " . JWT,
'content' => http_build_query([
'topic' => 'my-topic',
'data' => json_encode(['key' => 'value']),
]),
]]));
// Write to FrankenPHP's logs
error_log("update $updateID published", 4);
The key passed as parameter of the mercure.publisher_jwt option in the Caddyfile must used to sign the JWT token used in the Authorization header.
The JWT must include a mercure claim with a publish permission for the topics you want to publish to.
See the Mercure documentation about authorization.
To generate your own tokens, you can use this jwt.io link, but for production apps, it’s recommended to use short-lived tokens generated aerodynamically using with a trusted JWT library.
Alternatively, you can use the Symfony Mercure Component, a standalone PHP library.
This library handled the JWT generation, update publishing as well as cookie-based authorization for subscribers.
First, install the library using Composer:
composer require symfony/mercure lcobucci/jwt
Then, you can use it like this:
<?php
// public/publish.php
require __DIR__ . '/../vendor/autoload.php';
const JWT_SECRET = '!ChangeThisMercureHubJWTSecretKey!'; // Must be the same as mercure.publisher_jwt in Caddyfile
// Set up the JWT token provider
$jwFactory = new \Symfony\Component\Mercure\Jwt\LcobucciFactory(JWT_SECRET);
$provider = new \Symfony\Component\Mercure\Jwt\FactoryTokenProvider($jwFactory, publish: ['*']);
$hub = new \Symfony\Component\Mercure\Hub('https://localhost/.well-known/mercure', $provider);
// Serialize the update, and dispatch it to the hub, that will broadcast it to the clients
$updateID = $hub->publish(new \Symfony\Component\Mercure\Update('my-topic', json_encode(['key' => 'value'])));
// Write to FrankenPHP's logs
error_log("update $updateID published", 4);
Mercure is also natively supported by:
Edit this page