ThinkPHP 6 中 OPTIONS 请求返回 405 是因框架前置逻辑拦截,未进入中间件;需在中间件 handle() 开头判断 isOptions() 并返回 204 响应,且 CORS 配置须避免 Nginx 与 PHP 层重复。

OPTIONS 请求直接返回 405 是因为框架没处理它
ThinkPHP 6 默认不识别 OPTIONS 方法,尤其在开启 method_filter 或使用资源路由时,会直接拦截并返回 405 Method Not Allowed。这不是中间件写错了,而是请求根本没走到中间件链里——它被框架前置逻辑截胡了。
必须在中间件 handle() 开头就做短路处理,不能等 $next($request) 才响应:
- 加判断:
if ($request->isOptions()) { return response('', 204); } - 别用
200,204更规范,避免 CDN 或代理对空响应体做额外处理 - 绝对不要写
return $next($request)后再判断,那样已经晚了 - 检查
app/config/app.php中'method_filter' => false,或至少把'OPTIONS'加进白名单数组
全局中间件注册了但 CORS 头没出现
中间件类写对了、handle() 也设了 header,但浏览器 Network 里看不到 Access-Control-Allow-Origin ——大概率是中间件压根没执行,或者执行了但响应头被覆盖。
关键点不在“怎么写”,而在“怎么挂”:
立即学习“PHP免费学习笔记(深入)”;
- 必须在
app/middleware.php全局中间件数组中显式添加,例如:\app\middleware\CorsMiddleware::class - 如果用了路由分组(比如
Route::group('api', ...)),中间件得挂到该分组上,否则非 API 路由不会走它 - 不要只在控制器里
return json()或提前exit,这会中断中间件链,header 就丢了 - 禁止用原生
header()函数,必须调用$response->header(),否则 TP6 的响应封装机制会丢弃它
带 credentials 的跨域请求始终失败
Access-Control-Allow-Credentials: true 一开,浏览器就报 “Origin can't be *”,不是后端漏配 header,而是策略级拒绝:只要启用了凭证(Cookie、Authorization),Access-Control-Allow-Origin 就不能是 *。
解决方式不是“换个值”,而是动态匹配或白名单校验:
- 前端发来的
Origin请求头必须和后端允许的域名**完全一致**(协议、域名、端口) - 中间件里别硬编码
'Access-Control-Allow-Origin' => '*',改用:$origin = $request->header('origin'); $response->header('Access-Control-Allow-Origin', $origin); - 但要注意:必须先校验
$origin是否在白名单内,否则有安全风险;白名单可存在config/cors.php,自己读取,框架不自动加载它 - 同时确保
Access-Control-Allow-Credentials和前端fetch({ credentials: 'include' })严格对应
Nginx 层和 PHP 层 CORS 配置冲突
如果 Nginx 已经加了 add_header Access-Control-Allow-Origin "*";,而 PHP 中间件又加一遍,浏览器可能报错 “The 'Access-Control-Allow-Origin' header contains multiple values”。
更麻烦的是,Nginx 的 add_header 默认不继承,子 location 块里若没重写,可能漏掉 OPTIONS 路径。
- 推荐方案:Nginx 只处理预检,用
if ($request_method = 'OPTIONS') { add_header Access-Control-Allow-Origin "*"; add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"; add_header Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With"; add_header Access-Control-Allow-Credentials "true"; return 204; } - PHP 层专注业务响应头,不再重复设
Access-Control-Allow-Origin等基础字段 - 验证是否冲突:用
curl -I http://your-api.com/api/test看响应头是否唯一、无重复



















