Swoole原生HTTP服务不自带CORS支持,须在onRequest回调中手动设置响应头并显式处理OPTIONS预检请求;header()函数无效,必须用$response->header()或withHeader();带凭证时需白名单校验Origin且禁止使用通配符。

直接结论:Swoole 原生 HTTP 服务不自带 CORS 支持,必须手动在 onRequest 回调里设置响应头,并显式处理 OPTIONS 预检请求——漏掉任一环节都会导致跨域失败。
为什么 header() 在 Swoole HTTP 服务里完全无效
Swoole 是常驻内存的异步服务器,header() 是 PHP-FPM/CGI 模式下向 Web 服务器(如 Apache/Nginx)传递响应头的机制,在 Swoole 中根本不起作用。所有响应头必须通过 $response->header() 或 $response->withHeader()(取决于你用的是 Swoole\Http\Response 还是 PSR-7 封装)设置。
常见错误现象:
写了 header('Access-Control-Allow-Origin: *') 却没生效,浏览器仍报错;或者只在某些路由生效,其他路由失效——本质是没统一在 onRequest 入口处设置。
- 必须在
onRequest回调开头或中间统一注入头,不能分散在业务逻辑里 - 若用
$response->header($key, $value),注意它不支持链式调用,需逐个设 - 若用 PSR-7 响应对象(如
nyholm/psr7),则必须用$response->withHeader()并返回新实例
如何正确响应 OPTIONS 预检请求
浏览器对带 Content-Type: application/json、Authorization 头或非 GET/POST 方法的请求,会先发一次 OPTIONS 请求。Swoole 若不拦截,该请求会继续执行后续逻辑(比如进路由匹配、查数据库),既浪费资源又可能出错。
必须在 onRequest 里判断并提前返回:
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
if ($request->getMethod() === 'OPTIONS') {
$response->status(204);
$response->header('Access-Control-Allow-Origin', 'https://fe.example.com');
$response->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
$response->header('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With');
$response->header('Access-Control-Allow-Credentials', 'true');
$response->end();
return;
}
-
status(204)+end()是标准做法,不能用echo ''或exit - 预检响应里的
Access-Control-Allow-Origin必须和主请求一致;若需支持凭证,这里也得设Access-Control-Allow-Credentials: true -
Access-Control-Allow-Headers要包含前端实际发送的自定义头,比如token、X-Trace-ID,漏一个就会预检失败
带凭证(withCredentials: true)时的域名限制
一旦前端设置了 credentials: 'include' 或 xhr.withCredentials = true,Swoole 后端就不能再用 Access-Control-Allow-Origin: * ——浏览器会直接拒绝,控制台报错明确指出 “The value of the 'Access-Control-Allow-Origin' header must not be the wildcard '*'”。
必须做 Origin 白名单校验:
$origin = $request->header['origin'] ?? '';
$allowedOrigins = ['https://admin.example.com', 'https://fe.example.com'];
if (in_array($origin, $allowedOrigins)) {
$response->header('Access-Control-Allow-Origin', $origin);
$response->header('Access-Control-Allow-Credentials', 'true');
}
- 不要用
parse_url($origin)['host']做模糊匹配,容易被绕过(如https://evil.com@real.example.com) - 若开发环境用
http://localhost:3000,生产环境用 HTTPS 域名,白名单里必须同时写全 -
Access-Control-Allow-Credentials只能是字符串'true',不能是布尔值true,否则 Swoole 会忽略
Nginx 反向代理时的透传陷阱
如果 Swoole 前面套了 Nginx,而 Nginx 没配好,OPTIONS 请求可能根本到不了 Swoole,Nginx 自己就返回 405 或 502。
关键配置项(放在 location 块内):
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin 'https://fe.example.com';
add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS';
add_header Access-Control-Allow-Headers 'Content-Type, Authorization';
add_header Access-Control-Allow-Credentials 'true';
add_header Access-Control-Max-Age 86400;
return 204;
}
proxy_set_header Origin $http_origin;
-
proxy_set_header Origin $http_origin必须加,否则 Swoole 收不到原始Origin头,白名单校验失效 - Nginx 的
add_header不会覆盖 Swoole 返回的同名头,但预检请求必须由 Nginx 拦截并返回,不能放行给后端 - 如果 Nginx 和 Swoole 都设了 CORS 头,优先级以 Nginx 为准;调试时建议先关掉 Nginx 的 CORS,确认 Swoole 层逻辑无误后再协同
最易被忽略的一点:Swoole 的 onRequest 是纯回调,没有中间件概念,所有逻辑都得自己组织;一旦漏判 OPTIONS、忘设 Access-Control-Allow-Credentials、或白名单没覆盖当前 Origin,跨域就静默失败——浏览器控制台只报 CORS 错误,不会告诉你哪一行代码没执行。

















