mercure topic命名必须严格匹配caddy路由规则,需为合法uri(含scheme、host),且caddyfile中topic指令须以/结尾实现前缀匹配;多租户需通过host或path嵌入标识,publish_origins需显式配置跨域源。

Mercure topic命名必须匹配Caddy路由规则
FrankenPHP内置的Mercure Hub不是独立服务,它直接复用Caddy的HTTP路由系统。这意味着你写的topic字符串,最终会被当作Caddy的path来匹配。如果Caddy没配置对应路由,publish或subscribe都会404。
常见错误现象:curl -X POST http://localhost/.well-known/mercure -d 'topic=http://example.com/foo&data=bar' 返回 404;前端用new EventSource('http://localhost/.well-known/mercure?topic=http%3A%2F%2Fexample.com%2Ffoo') 一直 pending。
- Caddyfile里必须显式声明
mercure块,并用topic指令白名单允许的URI前缀 - topic值不能是任意字符串,必须是合法URI(含scheme、host),例如
https://api.example.com/orders/{id},不能写成orders/123或/orders/123 - 路径部分支持占位符
{id},但只在publish时生效;subscribe端仍需传完整匹配的URI
PHP代码里调用publish()时topic参数要严格一致
FrankenPHP的PHP SDK(即frankenphp_mercure_publish())不校验topic格式,但它会原样转发给Caddy。一旦PHP里拼错大小写、多一个斜杠、少一个协议,Caddy就无法匹配到已配置的topic规则。
使用场景:Laravel中用frankenphp_mercure_publish()推送订单更新,但前端收不到事件。
- 检查PHP中构造的topic是否和Caddyfile里
topic行完全一致(包括https://、域名、尾部斜杠) - 避免动态拼接出非法URI,比如
$topic = 'https://' . $host . '/orders/' . $id;—— 若$host含端口或路径,结果就不是标准URI - 推荐用
parse_url()验证后再传入,或直接硬编码基础前缀,ID用urlencode()处理
多租户场景下topic必须带可区分标识
FrankenPHP的Mercure Hub默认共享全局topic空间。如果你跑多个应用(如app-a.example.com和app-b.example.com),共用同一个FrankenPHP实例,topic不加隔离会导致事件错发。
性能影响:无额外开销,但逻辑上必须靠命名隔离,否则无法实现租户间消息边界。
- 在topic URI的host或path中嵌入租户标识,例如
https://app-a.example.com/notificationsvshttps://app-b.example.com/notifications - 不要依赖query参数做区分(如
?tenant=app-a),因为Caddy的topic指令不解析query - 若用子路径部署(
/app-a/,/app-b/),topic应为https://example.com/app-a/notifications,确保Caddy路由和topic白名单同步覆盖
别忽略Caddy的publish_origins限制
即使topic命名完全正确,前端仍可能被拒绝订阅——因为Caddy默认只允许同源(Origin头匹配host)发起EventSource连接。跨域调试时容易卡在这里。
错误信息:Failed to construct 'EventSource': The request has been blocked by CORS policy 或 Mercure Hub日志里出现origin not allowed。
- 开发时可在Caddyfile的
mercure块里加publish_origins ["*"],但生产环境必须明确列出可信源 -
publish_origins控制的是HTTP请求头Origin,和topic命名无关,但两者共同决定一次订阅是否成功 - 如果前端跑在
http://localhost:3000,而FrankenPHP在https://api.example.com,就必须把http://localhost:3000加入publish_origins
topic白名单是前缀匹配,但必须以/结尾才匹配子路径。写成topic https://api.example.com/orders不会匹配https://api.example.com/orders/123,得写成topic https://api.example.com/orders/。php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











