FrankenPHP内置Mercure模块,无需单独进程,启用只需Caddyfile中添加mercure指令;其依赖HTTPS,本地开发需用https://localhost或配置真实证书;JWT密钥须与Caddy配置一致以避免401错误。

Mercure在FrankenPHP里不需要单独起进程
FrankenPHP内置的Mercure不是外部服务,而是作为Caddy的一个原生模块直接集成在同一个二进制里。你只要在Caddyfile中启用它,FrankenPHP启动时就会自动拉起Mercure Hub,不依赖额外的mercure命令或独立容器。
常见错误现象是:有人照着旧版Mercure文档去跑mercure run,结果端口冲突(默认8080),或者发现/mercure路由 404——其实是因为没在Caddy配置里声明启用,而不是服务没起来。
- 启用方式很简单,在Caddyfile的站点块里加一行:
mercure - 如需自定义配置(比如JWT密钥、allowed_origins),用
mercure { ... }代码块包裹 - 环境变量
MERCURE_PUBLISHER_JWT_KEY和MERCURE_SUBSCRIBER_JWT_KEY会被自动读取,优先级高于Caddyfile内硬编码值 - HTTP/3支持下,Mercure的SSE推送也能走QUIC,但客户端必须用支持
text/event-stream+ HTTP/3的浏览器(如Chrome 125+)
为什么本地开发时Mercure看似“不工作”
因为FrankenPHP默认只对localhost或127.0.0.1签发本地HTTPS证书,而Mercure要求所有通信走HTTPS(包括前端EventSource连接)。如果你用http://myapp.test访问,浏览器会拒绝建立SSE连接,并报错EventSource's response has a MIME type ("text/plain") that is not "text/event-stream"或直接被CORS拦截。
解决方法只有两个:
立即学习“PHP免费学习笔记(深入)”;
- 用
https://localhost访问(Caddy自动提供证书,接受自签名即可) - 或在Caddyfile里显式配置域名+真实证书路径,例如:
tls /data0/Server/Auths/certs/myapp.test.crt /data0/Server/Auths/certs/myapp.test.key
Worker模式下Mercure订阅状态是否跨请求保持
不保持。Mercure Hub本身是无状态的,每个EventSource连接对应一个独立HTTP长连接,由Caddy底层的Go net/http维持。但PHP worker进程里的订阅逻辑(比如Laravel MercureBundle里的subscribe()调用)不会自动复用——它只是向Hub发起一次注册请求,后续推送由Hub直连浏览器,和PHP进程生命周期无关。
也就是说:
- 你不需要在worker循环里反复调用
mercure_subscribe() - 也不用担心worker重启导致订阅丢失(Hub不依赖PHP进程存活)
- 真正要注意的是:PHP端生成JWT票据的密钥必须和Caddy配置里的
MERCURE_PUBLISHER_JWT_KEY完全一致,否则401 Unauthorized
mercure.yaml、一个systemd service、一套证书同步逻辑。但这也意味着它的行为完全受Caddy控制,调试时得盯着caddy.log而不是mercure.log。



















