workerman跨域需手动配置cors头并显式处理options预检请求,因route::post()等限定方法路由会直接拒绝options导致预检失败;必须用route::any()或显式声明options方法,且access-control-allow-origin不能为*时启用credentials,须动态校验origin白名单并确保nginx透传origin头。

Workerman 本身不自动处理跨域,必须手动在 $connection->header() 中设置 CORS 响应头,且必须显式响应 OPTIONS 预检请求,否则浏览器直接拦截。
为什么加了 Access-Control-Allow-Origin 还报错?
常见现象是控制台报 No 'Access-Control-Allow-Origin' header is present on the requested resource,但你明明写了 $connection->header('Access-Control-Allow-Origin: *')。根本原因往往是:OPTIONS 请求没被正确捕获或提前返回,导致后续真实请求根本发不出去。
- 浏览器对非简单请求(如带
Authorization、Content-Type: application/json或自定义 header 的 POST)会先发一次OPTIONS预检 - Workerman 的
$http_worker->onMessage默认只处理实际业务逻辑,OPTIONS请求进来后若没显式return,就会继续往下走,可能触发 404 或空响应,此时 CORS 头根本没发出去 - 不能依赖 Nginx 或前端加 header 解决——CORS 是服务端响应头,由 Workerman 进程生成并发出
怎么写才真正生效?关键三步缺一不可
以下代码片段必须同时满足,才能让跨域请求完整跑通:
- 在
onMessage回调开头判断$_SERVER['REQUEST_METHOD'] === 'OPTIONS',立即$connection->send('')并return - 对所有请求(包括
OPTIONS)都调用$connection->header()设置 CORS 头,顺序不重要,但不能漏 -
Access-Control-Allow-Origin值不能是*同时又设Access-Control-Allow-Credentials: true,否则浏览器拒绝——要么去掉 credentials,要么动态匹配 Origin
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
$connection->header('Access-Control-Allow-Origin: https://your-frontend.com');
$connection->header('Access-Control-Allow-Credentials: true');
$connection->header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');
$connection->header('Access-Control-Allow-Headers: Content-Type, Authorization');
$connection->send('');
return;
}
// 后续业务逻辑前再设一遍(确保非 OPTIONS 请求也有头)
$connection->header('Access-Control-Allow-Origin: https://your-frontend.com');
$connection->header('Access-Control-Allow-Credentials: true');
Webman 用户注意:路由写法决定 CORS 能不能过预检
如果你用的是 Webman(基于 Workerman 的框架),光配中间件不够,Route::post() 或 Route::get() 会直接拒绝 OPTIONS 请求,导致预检失败。
- 必须把需要跨域的接口路由改成
Route::any()或显式声明Route::add(['POST', 'OPTIONS'], ...) - 如果用了分组路由,比如
Route::group('/api', function () { ... }),里面每个子路由也得是any,不能只写post - 中间件里的
Access-Control-Allow-Origin设置只是补充,不解决路由层拦截OPTIONS的问题
生产环境别用 *,Origin 必须精确匹配或白名单校验
开发时写 Access-Control-Allow-Origin: * 看似省事,但上线后一旦业务要传 cookie 或 token,就必须关掉 *,否则浏览器报错 The value of the 'Access-Control-Allow-Origin' header must not be the wildcard '*' when the request's credentials mode is 'include'。
- 简单方案:从
$_SERVER['HTTP_ORIGIN']取值,硬编码白名单数组比对,匹配则回写该 Origin - 注意:
HTTP_ORIGIN在OPTIONS请求里一定存在,但在某些代理或 CLI 测试中可能为空,需判空 fallback - 不要用
$_SERVER['HTTP_REFERER']替代——它不可靠,且不是 CORS 规范要求的字段
最易忽略的一点:Nginx 反向代理时,如果配置了 proxy_pass 却没透传原始 host 和 origin,Workerman 拿到的 $_SERVER 可能被污染。务必确认 Nginx 配置里有 proxy_set_header Origin $http_origin; 和 proxy_set_header Host $host;。











