Skip to main content
Version: 2026.2

Mercure Setup

Direct Edit needs a running Mercure hub. This page covers running the hub itself: the Docker image, the reverse proxy, and the questions that come up around both.

info

Pimcore Studio also uses Mercure, and the Studio Backend bundle documents how Pimcore connects to it. Follow Studio Backend Mercure Setup for the Pimcore-side configuration, then use this page for the hub.

Direct Edit additionally requires the JWT key parameters described in Installation & Configuration.

Start and Configure Mercure

Run an up-to-date Mercure instance, for example the official Docker container. See the Mercure docs for the installation details.

Configure JWT Key(s)

Mercure needs JWT keys to sign and verify the tokens used for authentication. Use one key for both roles (jwt_key), or separate keys for publisher (publisher_jwt_key) and subscriber (subscriber_jwt_key).

parameters:
mercure:
hub:
jwt_key: 'YOUR_JWT_KEY_FOR_PUBLISHER_AND_SUBSCRIBER'
#publisher_jwt_key: 'YOUR_JWT_KEY_FOR_PUBLISHER'
#subscriber_jwt_key: 'YOUR_JWT_KEY_FOR_SUBSCRIBER'

Configure Mercure URLs (optional)

Configure the URLs for accessing Mercure server-side (for updating state information within application services) and client-side (for receiving updates in Pimcore Studio) through the Symfony configuration tree:

pimcore_direct_edit:
mercure_settings:

# URL of mercure accessible for client. If not set, default will be set to 'http(s)://<PIMCORE_HOST>/hub/.well-known/mercure'. It is possible to use '<PIMCORE_SCHEMA_HOST>' as placeholder for current schema and host.
client_side_url: null

# URL of mercure accessible for server. If not set, default will be set to 'http(s)://<PIMCORE_HOST>/hub/.well-known/mercure'.
server_side_url: null

Configure the full URL including protocol, port and path. client_side_url also accepts <PIMCORE_SCHEMA_HOST> as a placeholder for the current scheme and host of the client. Left unset, both URLs are derived from the Pimcore host and the default paths.

To let the JWT cookie cover subdomains as well, set its host and strictness.

pimcore_direct_edit:
mercure_settings:
client_side_url: 'mercure.app.main.domain/hub/.well-known/mercure'

server_side_url: 'mercure.app.main.domain/.well-known/mercure'

jwt_cookie_host: 'app.main.domain' # It can also be a main.domain

jwt_cookie_strictness: false


Example Configuration with docker compose

The snippets below set up Mercure with docker compose. Mercure is exposed only through an nginx reverse proxy running over HTTPS; internal communication runs over HTTP, which helps when working with self-signed certificates in development.

docker-compose.yaml

Add mercure to your docker compose file and make sure to add the configuration for JWT keys.

  # add here all the other necessary containers... 

# configure nginx to run under https
nginx:
image: nginx:stable-alpine
ports:
- 443:443
depends_on:
- php-fpm
volumes:
- ./demo-px-enterprise:/var/www/html:ro

# mount nginx configuration to be adapted (see below)
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro

# certificates for running nginx (for direct edit to work, system has to run with https)
- ~/.certs:/etc/nginx/certs

mercure:
image: dunglas/mercure
restart: unless-stopped
environment:
# Disable HTTPS
SERVER_NAME: ':80'

# Add JWT keys configured
MERCURE_PUBLISHER_JWT_KEY: '<YOUR_JWT_KEY>'
MERCURE_SUBSCRIBER_JWT_KEY: '<YOUR_JWT_KEY>'


nginx.conf

Configure reverse proxy in nginx to route mercure traffic accordingly.

server {

# (...)

location /hub {
proxy_pass http://mercure/.well-known/mercure;
}

# Thumbnails
# (...)

}

config.yaml

Configure Pimcore application with JWT key as well as the client side and server side URLs for mercure.

parameters:
mercure:
hub:
jwt_key: '<YOUR_JWT_KEY>'

pimcore_direct_edit:
mercure_settings:
# URL of mercure accessible for client.
client_side_url: <PIMCORE_SCHEMA_HOST>/hub
# URL of mercure accessible for server.
server_side_url: http://mercure/.well-known/mercure

Check if Mercure is running

Call https://your-app-domain.com/hub/.well-known/mercure (or the client_side_url you configured). The response has to be:

Missing "topic" parameter.

Run the same request from the server's command line against server_side_url, to confirm the hub is reachable by Pimcore itself:

curl https://your-app-domain.com/hub/.well-known/mercure

Additional Aspects

Running Mercure with external URL

When running Mercure as external service with external URL, make sure to configure CSP properly.

Pimcore Configuration

pimcore_studio_ui:
csp_header:
additional_urls:
connect-src:
- 'https://<MERCURE_URL>/.well-known/mercure'

Mercure Configuration

Sample based on docker compose, important part is the cors_origins directive.

  mercure:
image: dunglas/mercure
restart: unless-stopped
environment:
# All other necessary configs ...
# ...

# Add cors configuration
MERCURE_EXTRA_DIRECTIVES: |-
cors_origins "https://<YOUR_PIMCORE_URL>"

Webserver Reverse Proxy

With the Mercure default URLs, a reverse proxy routes the Mercure requests to the Mercure instance. Communication has to run over HTTPS, so place the Mercure server behind the reverse proxy of the web server that handles the certificates.

Apache Reverse Proxy

Enable http_proxy in Apache and add this reverse proxy to the Apache config:

   ProxyPass /hub/ http://mercure/.well-known/mercure
ProxyPassReverse /hub/ http://mercure/.well-known/mercure

Nginx Reverse Proxy

For Nginx:

	location /hub {
proxy_pass http://mercure/.well-known/mercure;
}

Legacy Fallback

warning

The bundle ships the Mercure binary for backwards compatibility only. It is untested, may no longer work, and may contain security vulnerabilities. Do not use it in production.

The bundle ships a Mercure executable as a fallback. Run it with:

./vendor/pimcore/direct-edit/bin/mercure --jwt-key=your-256-bit-secret-min-32-chars --addr=':3000' --debug --allow-anonymous --cors-allowed-origins='*' -f --debug

The hub has to stay up permanently. This crontab entry restarts it if it died:

*/5 * * * * /usr/bin/flock -n /tmp/mercure.lockfile /<system-path>/mercure --jwt-key=your-256-bit-secret --addr=':3000' --debug --allow-anonymous --cors-allowed-origins='*' -f