hyperf中corsmiddleware失效主因是options预检请求被路由层拦截返回405,未进入中间件;需全局注册中间件、启用auto_options、校验origin白名单,并从context获取response对象。

Hyperf里加了 CorsMiddleware 却没生效,基本不是头写错了,而是中间件压根没执行——尤其是 OPTIONS 预检请求被路由层拦截后直接返回 405 Method Not Allowed,连中间件的边都没摸到。
为什么 OPTIONS 请求卡在路由层就返回 405
Hyperf 默认不自动注册 OPTIONS 路由。浏览器发来预检请求时,如果当前路径(比如 /api/user)没在路由中显式声明过 OPTIONS 方法,hyperf/routing 会抛出 MethodNotAllowedHttpException 并响应 405,此时整个中间件链已中断。
- 检查日志里有没有
MethodNotAllowedHttpException或405 Method Not Allowed - 确认你访问的路径是否在路由中显式声明过
OPTIONS,哪怕只写Router::addRoute('OPTIONS', '/api/user', []) - Hyperf 3.1+ 可启用
auto_options:在config/autoload/routes.php里设'auto_options' => true,它会自动为所有已注册的GET/POST/PUT等路径补上OPTIONS
中间件必须全局注册且顺序靠前
CorsMiddleware 如果只通过 @Middleware 注解或 middleware 数组配给某个 Router::addGroup,那未覆盖的路径(尤其是根路径 /、健康检查 /ping)照样掉进 405 陷阱。
- 必须把中间件注册到全局:在
config/autoload/middlewares.php的'http'数组顶层添加,例如:App\Middleware\CorsMiddleware::class - 别写成
['http' => ['App\Middleware\CorsMiddleware']]这种嵌套错误格式,应是'http' => [App\Middleware\CorsMiddleware::class] - 若同时用了鉴权中间件(如
AuthMiddleware),CorsMiddleware必须放在它前面——否则预检请求先被鉴权拦住,根本走不到 CORS 逻辑
Response 对象必须从 Context 获取,不能 new
很多示例直接 new \Hyperf\HttpMessage\Server\Response(),这会创建一个脱离上下文的新对象,后续 withHeader() 操作全白费,Header 不会出现在最终 HTTP 响应里。
- 必须从上下文取当前响应:
$response = Context::get(ResponseInterface::class) - 所有头操作要链式调用后,再写回上下文:
Context::set(ResponseInterface::class, $response->withHeader(...)) - 处理
OPTIONS分支时,除了withStatus(204),还必须withBody(new SwooleStream('')),否则 Swoole 协程可能卡住连接
生产环境 Origin 白名单不可省略
浏览器明确禁止 Access-Control-Allow-Origin: * 和 Access-Control-Allow-Credentials: true 同时存在。开发时图省事这么写,上线就会发现带 cookie 的请求始终被拦。
- 必须做
Origin白名单校验:if (in_array($origin, $this->allowedOrigins)) { ... } - 响应头里
Access-Control-Allow-Origin值应设成具体域名(如https://your-app.com),不能是* - 注意:预检响应中的
Access-Control-Allow-Headers必须精确包含前端实际发送的自定义头,比如Authorization、X-Request-ID,漏一个都会导致预检失败
最易被忽略的是:OPTIONS 分支里漏掉 withBody(new SwooleStream('')),Swoole 连接会挂住;还有生产环境硬编码 * 导致凭据模式彻底失效——这两个点一旦出问题,日志里几乎不报错,只能靠抓包看响应体是否为空、看浏览器控制台是否提示“CORS header ‘Access-Control-Allow-Origin’ missing”。











