ThinkPHP 8.0 跨域预检失败需配置 Cors 中间件:拦截 OPTIONS 请求返回 204 并设置 Access-Control 响应头,中间件须置于 middleware.php 数组首位;带 credentials 时禁止用 *,需白名单校验 origin 并动态设置响应头。

前端发起带 Authorization 头或 Content-Type: application/json 的请求时,浏览器强制触发 OPTIONS 预检却返回 405 或无响应,导致真实请求被拦截——这不是前端写错了,而是 ThinkPHP 8.0 没拦截预检请求或响应头缺失。
创建并注册 Cors 中间件
执行命令行生成中间件文件:php think make:middleware Cors。
打开 app/middleware/Cors.php,将 handle 方法替换为以下内容:
if ($request->isOptions()) {
return response('', 204)
->header('Access-Control-Allow-Origin', $request->header('origin') ?: '*')
->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
->header('Access-Control-Allow-Headers', 'Authorization, Content-Type, X-Requested-With')
->header('Access-Control-Allow-Credentials', 'true');
}
$response = $next($request);
$response->header('Access-Control-Allow-Origin', $request->header('origin') ?: '*');
$response->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
$response->header('Access-Control-Allow-Headers', 'Authorization, Content-Type, X-Requested-With');
$response->header('Access-Control-Allow-Credentials', 'true');
return $response;
立即学习“PHP免费学习笔记(深入)”;
【必须把 Cors::class 放在 app/middleware.php 数组最前面】,否则 SessionMiddleware 或 JWT 验证中间件可能提前输出响应,导致 headers already sent 错误。
处理带凭证(Cookie/Auth)的跨域请求
前端设置了 credentials: 'include',但浏览器报错 “Credentials flag is true, but the 'Access-Control-Allow-Origin' value is not the literal '*'”——这是硬性限制,不能绕过。
第一步:禁止使用通配符 * 作为 Access-Control-Allow-Origin 值。
第二步:动态读取 Origin 请求头,并只允许白名单域名通过:
复制以下代码替换中间件中 origin 相关逻辑:
$origin = $request->header('origin');
$allowedOrigins = ['https://admin.example.com', 'http://localhost:3000'];
$origin = in_array($origin, $allowedOrigins) ? $origin : null;
第三步:仅当 $origin 不为 null 时才设置响应头,否则不返回任何 CORS 头——避免暴露敏感策略。
第四步:显式启用凭据支持:$response->header('Access-Control-Allow-Credentials', 'true');
验证 OPTIONS 预检是否真正生效
方法一:用 curl 手动触发预检请求:
curl -I -X OPTIONS http://your-domain.com/api/v1/users
检查响应中是否包含 Access-Control-Allow-Origin 和状态码是否为 204。
方法二:在 Chrome 开发者工具 Network 标签页中,筛选 OPTIONS 请求,点击它,查看 Response Headers 区域。
如果看到 HTTP/2 404 或 HTTP/2 500,说明路由未匹配到 OPTIONS 方法,【不是中间件问题,是路由层缺失处理路径】。
方法三:临时在中间件 handle 开头加一行 file_put_contents('/tmp/cors.log', "OPTIONS hit\n", FILE_APPEND);,然后发起跨域请求,检查日志是否写入——能写入说明中间件已执行,失败点在 header 设置或响应返回环节。



















