CHECKING STATUS
I AM LISTENING TO
|

Day 36: mercure – Open-Source protocol for pushing data updates via SSE – 7 Days of Docker

29. August 2025
.SHARE

Table of Contents

Mercure is an open-source protocol for pushing data updates to web browsers and other HTTP clients in real-time. It’s built on top of Server-Sent Events (SSE) and provides a fast, reliable, and battery-efficient way to implement real-time communications. The Mercure hub is distributed as a custom build of the Caddy web server with the Mercure module included.

In this guide, we’ll walk through setting up Mercure on your own server using Docker Compose, covering all the essential configuration options and environment variables you need to get started.

Basic Docker Compose Setup

Here’s a minimal Docker Compose configuration to get Mercure running:

Development Configuration

For development purposes, you can enable the debug UI and allow anonymous subscribers:

Production Configuration with Health Checks

For production deployments, include health checks and proper security settings:

HTTP-Only Setup (Behind Reverse Proxy)

If you’re running Mercure behind a reverse proxy and want to disable HTTPS:

Environment Variables Reference

Core Configuration

  • SERVER_NAME: The server name or address (default: localhost)
    • Examples: localhost, mercure.example.com, :3000, :80
  • MERCURE_PUBLISHER_JWT_KEY: JWT key for publishers (required)
  • MERCURE_SUBSCRIBER_JWT_KEY: JWT key for subscribers (required)
  • MERCURE_PUBLISHER_JWT_ALG: JWT algorithm for publishers (default: HS256)
  • MERCURE_SUBSCRIBER_JWT_ALG: JWT algorithm for subscribers (default: HS256)

Advanced Configuration

  • MERCURE_EXTRA_DIRECTIVES: Additional Mercure directives (multiline)
  • CADDY_EXTRA_CONFIG: Additional Caddy configuration
  • CADDY_SERVER_EXTRA_DIRECTIVES: Additional Caddy server directives
  • GLOBAL_OPTIONS: Global Caddy options block

JWT Configuration Options

You can use different JWT algorithms:

Complete Production Example

Here’s a comprehensive production setup with all common options:

Environment File Setup

Create a .env file for your secrets:

Mercure Directives Explained

Security Directives

  • anonymous: Allow subscribers without JWT tokens
  • cors_origins <origins>: Allowed CORS origins
  • publish_origins <origins>: Origins allowed to publish (cookie auth only)

Performance Directives

  • heartbeat <duration>: Interval between heartbeats (default: 40s)
  • dispatch_timeout <duration>: Max dispatch time per update (default: 5s)
  • write_timeout <duration>: Max connection duration (default: 600s)
  • transport <config>: Storage backend configuration

Feature Directives

  • ui: Enable the debug UI
  • demo: Enable demo endpoints
  • subscriptions: Enable subscription API

Accessing Your Mercure Hub

Once running, your Mercure hub will be available at:

  • Subscribe endpoint: GET https://your-domain/.well-known/mercure
  • Publish endpoint: POST https://your-domain/.well-known/mercure
  • Health check: GET https://your-domain/healthz
  • Debug UI (if enabled): GET https://your-domain/.well-known/mercure/ui/

Starting Your Setup

  1. Save your Docker Compose configuration to compose.yaml
  2. Create your .env file with secure JWT keys
  3. Generate strong JWT keys (JWT Encoder / Decoder):
  1. Start the service:
  1. Check the logs:

Generating JWT Tokens

To publish updates, you’ll need to generate JWT tokens. Here’s a simple example in multiple languages:

PHP

JavaScript/Node.js

Python

Publishing Updates

Once you have a JWT token, you can publish updates using a simple HTTP POST request:

Subscribing to Updates

Clients can subscribe to updates using Server-Sent Events:

Advanced Configuration Examples

Multiple Topics and Private Updates

High Availability Setup

Troubleshooting Common Issues

CORS Issues

If you’re experiencing CORS problems, make sure to configure the cors_origins directive properly:

JWT Token Issues

Ensure your JWT tokens have the correct structure:

Connection Issues

Check if the Mercure hub is accessible:

Security Best Practices

  1. Use strong JWT keys: Generate random, long keys for production
  2. Restrict CORS origins: Only allow trusted domains
  3. Use HTTPS: Always enable TLS in production
  4. Limit topic access: Use specific topic patterns in JWT tokens
  5. Regular key rotation: Rotate JWT keys periodically
  6. Monitor access logs: Keep track of connections and publications

Performance Optimization

Transport Configuration

Optimize the Bolt database settings for your use case:

Timeout Configuration

Important Links

Your Mercure hub is now ready to handle real-time communications for your applications!

EXTRA: Setting Up Mercure with Caddy and NGINX Proxy Manager

1. Mercure Configuration with Caddy

First, let’s configure Mercure with its built-in Caddy server. Create a Caddyfile:

Or using environment variables in docker-compose.yml:

2. NGINX Proxy Manager Configuration

In NGINX Proxy Manager, create a proxy host with these settings:

Details Tab:

  • Domain Names: mercure.yourdomain.com
  • Scheme: http
  • Forward Hostname/IP: mercure (or the container name/IP)
  • Forward Port: 3000
  • Enable “Websockets Support” ✓ (Important!)
  • Enable “Block Common Exploits” ✓
  • Enable “HTTP/2 Support” ✓

SSL Tab:

  • Request SSL Certificate with Let’s Encrypt
  • Force SSL ✓

Advanced Tab – Custom Nginx Configuration:

3. Working CORS Configuration for NGINX Proxy Manager

Option 1: Simple CORS Configuration (Recommended)

In the Advanced Tab of your proxy host, add this configuration that works within NPM’s limitations:

Option 2: Let Mercure Handle CORS (Cleaner Approach)

Since Mercure has built-in CORS support, you can configure CORS entirely in Mercure and just focus on proxy settings in NPM:

NGINX Proxy Manager Advanced Config:

Mercure Docker Configuration:

Option 3: Using Custom Location Block

Some versions of NPM allow custom location blocks. Try this in the Advanced tab:

4. Complete Docker Compose Setup

Here’s a complete setup with both services:

5. Testing Your Configuration

Test Mercure directly:

Test CORS:

Check if Mercure is accessible:

Test CORS headers are present:

You should see headers like:

JavaScript client example:

Test with a simple HTML client:

6. Important Settings Summary

Must-have in NPM Advanced Config:

  • proxy_buffering off – Critical for SSE
  • proxy_read_timeout 24h – Prevents timeout
  • add_header 'X-Accel-Buffering' 'no' – Disables NGINX buffering

In NPM Details Tab:

  • Enable “Websockets Support”
  • Use HTTP scheme (not HTTPS) if Mercure is on same network
  • Set correct port (3000 by default)

7. Troubleshooting Common Issues

SSE Connection Drops:

  • Ensure proxy_buffering off is set
  • Check proxy_read_timeout is set to a high value (24h)
  • Verify WebSockets support is enabled in NGINX Proxy Manager

CORS Errors:

  • Check origins match exactly (including protocol)
  • Verify credentials setting matches on both client and server
  • Check browser console for specific CORS error messages

Authentication Issues:

  • Verify JWT keys match between publisher and Mercure config
  • Check JWT token expiration
  • Ensure JWT has correct claims for publishing/subscribing

The key is that NPM’s GUI is limited, so it’s often better to let Mercure handle CORS entirely and just ensure NPM properly proxies the SSE connections without buffering.

FAQ SSE

What are Server-Sent Events (SSE)?

Server-Sent Events (SSE) is a web technology that enables a server to push data to a client over HTTP. It allows servers to maintain data transmission to clients after an initial connection has been established, providing real-time updates like stock prices, news feeds, or live notifications without requiring the client to poll the server.

How do SSE differ from WebSockets?

SSE provides unidirectional communication (server-to-client only), while WebSockets support full bidirectional communication. SSE uses standard HTTP/HTTPS protocols and is simpler to implement, whereas WebSockets use their own protocol (ws:// or wss://) and require more complex setup. SSE has automatic reconnection built-in, while WebSockets require manual reconnection handling.

What is the connection limit for SSE?

When using HTTP/1.1, browsers limit SSE connections to 6 concurrent connections per domain per browser. This limit applies across all tabs. However, with HTTP/2, the limit increases to around 100 simultaneous streams (negotiated between client and server), effectively solving the connection limitation problem.

How do I implement SSE on the client side?

Use the EventSource API in JavaScript:

How do I implement SSE on the server side?

Set the proper headers and send event-formatted data:

Why do SSE connections keep disconnecting?

Common causes include: network timeouts, proxy/firewall interference, server-side timeouts, or antivirus software (like Sophos) blocking the connection. Solutions include implementing keep-alive messages (sending comments like “: keep-alive\n\n” every 15-30 seconds), configuring proxy settings, or adjusting server timeout settings.

What browsers support SSE?

SSE is supported by all modern browsers including Chrome, Firefox, Safari, Edge, and Opera. Internet Explorer does not support SSE natively, but polyfills are available. Browser support has been stable since around 2011 for most major browsers.

When should I use SSE instead of WebSockets?

Use SSE when you need:
  • One-way server-to-client communication only
  • Simple implementation with existing HTTP infrastructure
  • Automatic reconnection handling
  • Real-time updates like news feeds, stock prices, or notifications
  • Better compatibility with firewalls and proxies
Choose WebSockets for bidirectional communication, gaming, chat applications, or when you need to send data from client to server frequently.

Can SSE send binary data?

No, SSE can only transmit UTF-8 text data. Binary data must be encoded (e.g., using Base64) before transmission, which increases overhead. If you need to send binary data efficiently, WebSockets would be a better choice.

How do I handle SSE reconnection?

SSE automatically attempts to reconnect when the connection is lost. You can control this behavior:

What are common SSE use cases?

Popular SSE use cases include:
  • Real-time news feeds and social media updates
  • Stock price tickers and financial data
  • Live sports scores and updates
  • System monitoring dashboards
  • Chat applications (receive messages only)
  • Progress indicators for long-running tasks
  • Live notifications and alerts
  • IoT sensor data streaming

How do I send different event types with SSE?

Use the ‘event:’ field to specify event types:

What are the performance limitations of SSE?

SSE limitations include:
  • 6 concurrent connections per domain on HTTP/1.1
  • Higher latency compared to WebSockets for frequent updates
  • Text-only data transmission (UTF-8)
  • One-way communication only
  • HTTP overhead for each message
  • Potential issues with corporate firewalls or proxies
Most limitations are resolved by using HTTP/2 or choosing WebSockets when appropriate.

How do I implement authentication with SSE?

Since SSE uses standard HTTP, you can use regular authentication methods:

How do I debug SSE connections?

You can debug SSE using:
  • Browser Developer Tools (Network tab shows EventSource connections)
  • curl command: curl -N -H "Accept: text/event-stream" http://your-server/events
  • Console logging in JavaScript event handlers
  • Server-side logging of connection states
  • Monitoring connection counts and error rates
The text-based format makes SSE easy to troubleshoot compared to binary protocols.
Let’s Talk!

Looking for a reliable partner to bring your project to the next level? Whether it’s development, design, security, or ongoing support—I’d love to chat and see how I can help.

Get in touch,
and let’s create something amazing together!

RELATED POSTS

If you’ve ever tried to style text in a WYSIWYG editor and ended up with HTML that looked like a spaghetti disaster, you’ve already met the reason why semantic editors meaning, structure, and sanity. In other words: they keep your content clean, organized, and machine-friendly. This article walks you through the most important semantic and […]

If you’ve ever worked with text editors on the web, you know the routine: dependencies everywhere, fragile integrations, and editors that only behave when the stars align. The Isolated Block Editor (IBE) solves this by taking the core of Gutenberg and making it work anywhere — no WordPress environment required. It gives you the power […]

Let’s be real—WordPress’s default table block is kind of… meh. It gets the job done for super basic tables, but the moment you need sorting, filtering, or something that doesn’t look like it’s stuck in 2010, you’re out of luck. That’s where table plugins come in, and trust me, there’s a whole world of options […]

Alexander

I am a full-stack developer. My expertise include:

  • Server, Network and Hosting Environments
  • Data Modeling / Import / Export
  • Business Logic
  • API Layer / Action layer / MVC
  • User Interfaces
  • User Experience
  • Understand what the customer and the business needs


I have a deep passion for programming, design, and server architecture—each of these fuels my creativity, and I wouldn’t feel complete without them.

With a broad range of interests, I’m always exploring new technologies and expanding my knowledge wherever needed. The tech world evolves rapidly, and I love staying ahead by embracing the latest innovations.

Beyond technology, I value peace and surround myself with like-minded individuals.

I firmly believe in the principle: Help others, and help will find its way back to you when you need it.