webman跨域必须在中间件中动态校验origin白名单并拦截options请求,因常驻内存导致header()失效,须用withheader()链式设置响应头且严格匹配协议+域名+端口。

Webman 的跨域必须用中间件动态校验 Origin,硬编码单域名或直接写 * 都会出安全问题——尤其当启用 Access-Control-Allow-Credentials: true 时,* 会被浏览器拒绝。
为什么 Webman 里 header() 失效?
因为 Webman 基于 Swoole 常驻内存,响应对象是 immutable 的。直接调用 header() 不会修改当前 $response,也不会被框架捕获;必须用 withHeader() 链式构造新响应并显式返回。
- 在控制器或路由闭包里写
header("Access-Control-Allow-Origin: *")→ 完全无效 - 没在中间件中拦截
OPTIONS请求 → 浏览器预检失败,后续请求被阻断 - 返回了
Response::create()但没加 CORS 头 → 预检响应不合法,仍报错
如何实现多域名白名单校验?
不能把白名单写死在中间件代码里,得支持运行时加载(比如从配置文件读取),同时要严格匹配协议+域名+端口,避免子域名泛滥或 Host 头伪造。
- 白名单配置建议放在
config/cors.php:返回数组如['https://app.example.com', 'https://admin.example.org:8080'] - 用
$request->getHeaderLine('Origin')获取原始Origin,不要依赖$_SERVER['HTTP_ORIGIN'](Swoole 环境下可能为空) - 校验时需完整字符串匹配,不建议用
parse_url()后比对 host —— 某些前端发请求时带端口,某些不带,容易漏判 - 若匹配成功,
Access-Control-Allow-Origin必须设为该Origin字符串原值,不能做任何截断或转换
OPTIONS 预检必须单独处理
浏览器对非简单请求(如带 Authorization、Content-Type: application/json 或自定义 header)会先发一次 OPTIONS。Webman 若不提前终止,业务逻辑仍会执行,既浪费资源又可能触发副作用。
- 必须在中间件
handle()开头判断:if ($request->getMethod() === 'OPTIONS') - 返回
Response::create('', 204),不是exit、不是空数组、也不是json(['code'=>0]) - 这个 204 响应里也要带上全部 CORS 头(
Allow-Origin、Allow-Methods、Allow-Headers),否则预检失败 - 别忘了设置
Access-Control-Max-Age,比如3600,减少重复 OPTIONS 请求
带 Cookie 的跨域必须关闭通配符
只要用了 withCredentials: true(前端 fetch 或 axios 配置),后端就绝不能返回 Access-Control-Allow-Origin: *,否则浏览器直接拒收响应。
- 即使白名单里只有一个域名,也得动态回写它,不能 fallback 到
* -
Access-Control-Allow-Credentials: true必须和具体Origin同时出现,缺一不可 - 如果白名单为空或未匹配到,应该返回 403 并省略所有 CORS 头(防止暴露策略),而不是返回
*试图“兜底” - 测试时注意:localhost 和 127.0.0.1 被视为不同源,开发环境的白名单得把两者都列进去
最易忽略的是预检响应的头完整性——少一个 Access-Control-Allow-Headers,哪怕只差一个字段名,浏览器就会判定预检失败;而 Webman 中间件一旦漏 return 修改后的 $response,整个链路就静默失效,连错误日志都不留。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











