FrankenPHP中Mercure的CORS报错源于响应头未透传,需显式配置Mercure的allowed_origins(禁用通配符)、在server.php中手动复制Access-Control-响应头,并确保前端origin与配置完全一致。

FrankenPHP里Mercure的CORS报错,本质是HTTP头没发出去
FrankenPHP默认不自动注入Mercure所需的CORS响应头,哪怕你配好了Mercure.PublishAllowedOrigins或Mercure.SubscriberAllowedOrigins,浏览器发起的fetch()或EventSource请求依然会被拦。原因很直接:FrankenPHP把Mercure当作独立服务代理(类似反向代理),它不会主动给Mercure响应加Access-Control-Allow-Origin这类头——这个活得你自己干,或者让Mercure自己吐出来。
Mercure Hub必须显式配置allowed_origins且不能用"*"
FrankenPHP本身不处理CORS逻辑,它只转发请求到Mercure Hub。所以关键在Mercure配置是否生效。常见错误是:
- 在
.env或mercure.yml里写了MERCURE_PUBLISH_ALLOWED_ORIGINS="*",但Mercure 0.14+已弃用*通配符对带凭证请求的支持 - 前端用
withCredentials: true或携带Authorization头,但allowed_origins仍设为*,导致浏览器直接拒绝预检 - 值写成
http://localhost:3000却漏了末尾/或协议不匹配(比如前端是https而配置是http)
正确做法是明确列出前端域名:MERCURE_SUBSCRIBER_ALLOWED_ORIGINS="http://localhost:3000,https://myapp.com"。注意逗号后不能有空格,且必须包含协议和端口(如果非标准端口)。
FrankenPHP的server.php里要手动透传Mercure响应头
FrankenPHP的server.php是PHP脚本入口,也是你能干预响应头的最后机会。Mercure Hub返回的响应头默认不会被FrankenPHP继承,必须显式复制。在调用new Mercure\Hub(...)之后、输出前插入:
立即学习“PHP免费学习笔记(深入)”;
// 假设你用的是官方SDK
$hub = new Mercure\Hub($publishUrl, $subscribeUrl);
$response = $hub->publish($update);
// 手动把Mercure响应头透传给浏览器
foreach ($response->getHeaders() as $name => $values) {
if (in_array(strtolower($name), ['access-control-allow-origin', 'access-control-allow-methods', 'access-control-allow-headers'])) {
foreach ($values as $value) {
header("$name: $value", false);
}
}
}
否则,即使Mercure Hub内部生成了正确的CORS头,FrankenPHP也会丢掉它们。
前端EventSource或fetch必须匹配Mercure的origin策略
浏览器对EventSource和fetch的CORS要求不同:
-
EventSource不支持withCredentials,所以allowed_origins可以设为*,但必须确保Mercure响应头含Access-Control-Allow-Origin: * -
fetch订阅时若带credentials: 'include',则allowed_origins必须是具体域名,且响应头Access-Control-Allow-Origin不能是*,否则浏览器静默失败 - 前端URL里的
origin必须和Mercure配置的allowed_origins完全一致,包括协议、大小写、末尾斜杠
最容易忽略的一点:FrankenPHP启动时若用php -S localhost:8000 server.php,浏览器地址栏是http://localhost:8000,但Mercure Hub可能监听http://127.0.0.1:3000,这时origin实际是http://localhost:8000,必须出现在allowed_origins里,而不是127.0.0.1。



















