workerman4 http服务必须显式处理options预检请求并为所有响应设置cors头:需在onmessage中判断method为options时返回204空响应及完整cors头,且业务响应也须重复设置access-control-allow-origin等头,禁用$connection->header()单独设头。

Workerman4 的 HTTP 服务在处理跨域请求时,必须显式支持预检(OPTIONS)请求,并为所有响应(包括预检和业务请求)设置正确的 CORS 响应头。只加一次头、或只在业务逻辑里加、或漏掉预检响应,都会导致前端报错。
必须拦截并响应 OPTIONS 预检请求
浏览器在发送带自定义头(如 Authorization)、非简单 Content-Type(如 application/json)或非 GET/POST 方法的请求前,会先发一个 OPTIONS 请求“探路”。Workerman 不会自动处理它,你得在 onMessage 中主动识别并返回空响应 + 正确头信息。
- 检查
$request->method() === 'OPTIONS',满足则立即 send 空响应并 return - 一定要调用
$connection->send('')或$connection->send(new Response(204)),不能只设 header 就结束 - 预检响应状态码推荐 204(No Content)或 200,但 body 必须为空
所有响应都要带 Access-Control-Allow-Origin
不只是预检需要跨域头,后续真正的 GET/POST 请求也必须携带,否则浏览器仍会拦截响应体。不能只在 OPTIONS 分支里设,业务逻辑分支里也要重复设置。
- 使用
Response类统一构造响应,便于控制 header 和状态码 - 不要用
$connection->header()单独设头,它不保证与 send 内容绑定,尤其在异步场景下容易丢失 - 若允许凭证(如 Cookie、Authorization),
Access-Control-Allow-Origin不能为*,必须指定具体域名,例如https://your-fe.com
完整 onMessage 跨域处理示例
以下代码覆盖预检 + 业务响应,使用 Response 类确保 header 可靠生效:
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
use Workerman\Protocols\Http\Request;
use Workerman\Protocols\Http\Response;
$worker = new Worker('http://0.0.0.0:8080');
$worker->onMessage = function (TcpConnection $connection, Request $request) {
$origin = $request->header('origin') ?: '';
$allowedOrigin = 'https://your-fe.com'; // 生产环境请严格校验
$isAllowedOrigin = in_array($origin, [$allowedOrigin, 'http://localhost:5173'], true);
// 处理预检请求
if ($request->method() === 'OPTIONS') {
$headers = [
'Access-Control-Allow-Origin' => $isAllowedOrigin ? $origin : '',
'Access-Control-Allow-Credentials' => 'true',
'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE, PATCH',
'Access-Control-Allow-Headers' => 'Content-Type, Authorization, X-Requested-With',
'Access-Control-Max-Age' => '86400',
];
$connection->send(new Response(204, $headers));
return;
}
// 业务请求:同样设置跨域头
$headers = [
'Access-Control-Allow-Origin' => $isAllowedOrigin ? $origin : '',
'Access-Control-Allow-Credentials' => 'true',
'Content-Type' => 'application/json; charset=utf-8',
];
// 示例业务响应
$data = ['code' => 0, 'msg' => 'success', 'data' => []];
$connection->send(new Response(200, $headers, json_encode($data, JSON_UNESCAPED_UNICODE)));
};
Worker::runAll();
常见踩坑点提醒
这些细节看似小,但线上联调时几乎必现:
- 本地开发用
localhost:5173访问,后端却只放行127.0.0.1:5173—— 浏览器认为这是两个不同源 - 启用了
withCredentials: true,但响应头中Access-Control-Allow-Origin还是*—— 浏览器直接拒绝 - Nginx 做了反向代理,但没透传 Origin 头,或自己加了跨域头覆盖了 Workerman 的设置 —— 最终响应头以 Nginx 为准
- 响应体用了
$connection->send('xxx')字符串方式,但没走 Response 类 —— header 无法附着,跨域头实际未发出











