frankenphp 的 mercure 需显式启用:启动时传参 --mercure-jwt-key 或设环境变量,caddyfile 中配置 mercure { publish_origins [...] },前端用 eventsource('/.well-known/mercure', {withcredentials: true}) 并确保 cors 与 topic uri 严格匹配。

FrankenPHP Mercure 需要显式启用,不是开箱即用
FrankenPHP 自带 Mercure 支持,但默认不启动。它不会像 Nginx + Mercure Hub 那样自动监听 /mercure;你必须在 Caddyfile 或命令行中明确配置 Mercure 服务端点,并指定 JWT 密钥和允许的发布者/订阅者。否则 new EventSource('/.well-known/mercure') 会直接 404 或返回 HTML 页面。
常见错误现象:Failed to construct 'EventSource': The response has unsupported MIME type ('text/html'),说明请求被路由到了 PHP 应用而非 Mercure Hub。
- 确保 Caddyfile 中包含
mercure { publish_origins ["https://your-domain.com"] }块(注意不是mercure /mercure) - FrankenPHP 启动时需传入
--mercure-jwt-key=your-secret-key,或通过环境变量MERCURE_JWT_KEY设置 - Mercure 的默认订阅路径是
/.well-known/mercure,不是/mercure;前端不要手动拼接路径 - 若使用自签名证书或本地开发,浏览器可能因 CORS 拒绝连接,需在
publish_origins中加入http://localhost:3000等实际来源
前端 EventSource 必须带 withCredentials 才能收 Mercure 消息
Mercure 订阅依赖 Cookie 中的 JWT 认证凭据(由 FrankenPHP 自动生成并注入),而原生 EventSource 默认不发送 Cookie。不加 withCredentials: true,服务端根本看不到认证信息,会直接拒绝订阅或返回空流。
错误写法:const es = new EventSource('/.well-known/mercure?topic=https://example.com/item/123'); —— 这会失败,且控制台无明显报错,只表现为“连接打开但无消息”。
- 正确写法:
const es = new EventSource('/.well-known/mercure?topic=https://example.com/item/123', { withCredentials: true }); - 服务端必须返回
Access-Control-Allow-Credentials: true,且Access-Control-Allow-Origin不能是*,必须精确匹配前端域名(如https://localhost:3000) - 如果 topic 是动态生成的,确保 URL 编码完整,例如
encodeURIComponent('https://api.example.com/users/42')
Topic URI 必须与服务端发布的主题严格一致
Mercure 使用 URI 作为 topic 标识,区分大小写、协议、路径、查询参数全部参与匹配。前端订阅 https://api.example.com/users/42,后端发布时用了 https://api.example.com/users/42/(末尾斜杠)或 http://,都会导致消息丢失。
典型陷阱:Laravel 或 Symfony 路由生成的 topic 带了 trailing slash,而前端硬编码没带;或开发环境用 http,生产环境用 https,但 topic 字符串没同步更新。
- 推荐统一用规范化的绝对 URI,避免相对路径或省略协议
- 可在服务端日志中确认实际发布的 topic(FrankenPHP 的 Mercure 日志会打印
published to topic "xxx") - 调试时可临时订阅通配符 topic:
/.well-known/mercure?topic=https://api.example.com/*,验证是否是 topic 匹配问题
不要用 fetch + ReadableStream 替代 EventSource 接 Mercure
Mercure 协议依赖浏览器内置的 EventSource 实现来解析 event:、data:、id: 和重连逻辑。手动用 fetch + ReadableStream 虽然能建立连接,但无法正确处理 Mercure 的事件类型分发(如 message vs patch)、Last-Event-ID 断线续传,以及服务端推送的 retry: 指令。
更关键的是:Mercure Hub 对非 EventSource 的客户端有额外限制——它会检查 User-Agent 或内部标识,对非标准流式请求可能降级为普通 HTTP 响应,或直接关闭连接。
- 坚持用原生
EventSource,仅在需要自定义 header(如 Bearer Token)时才考虑fetchEventSource库 - FrankenPHP Mercure 不支持
Authorizationheader 认证,只认 Cookie;强行改用 fetch 并加 header 会导致 401 - 若必须绕过 Cookie(如跨域无凭证场景),应改用 Mercure 的
JWT订阅模式,由服务端生成预签名 URL,而不是前端自己构造请求头
最易忽略的一点:FrankenPHP 的 Mercure 服务与 PHP 应用共享同一个进程和配置,但它的健康检查端点(如 /mercure/health)默认不暴露。调试时别只盯着 PHP 日志,得看 FrankenPHP 启动时的 stdout 是否输出 Mercure hub started —— 没这句,说明 Mercure 根本没起来,后面所有前端代码都是徒劳。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











