tp6跨域报错主因是后端未在正确时机用正确方式设置cors响应头,需拦截options预检、动态匹配origin、操作response设头,并避免nginx/apache重复添加;推荐手写cors中间件。

TP6中间件跨域报错,核心不是前端发错了请求,而是后端没在正确时机、用正确方式把 CORS 响应头发出去。重点在于拦截 OPTIONS 预检、动态匹配 Origin、操作 Response 对象设头,且避开 Nginx/Apache 重复添加。
手写 Cors 中间件(推荐,兼容 TP6.0+)
新建 app/middleware/Cors.php,内容如下:
namespace app\middleware;
use think\Request;
use think\Response;
class Cors
{
public function handle(Request $request, \Closure $next): Response
{
// 拦截并响应 OPTIONS 预检请求
if ($request->isOptions()) {
return response('', 204)
->header('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS')
->header('Access-Control-Allow-Headers', 'Authorization,Content-Type,X-Requested-With,X-Token')
->header('Access-Control-Allow-Credentials', 'true');
}
$response = $next($request);
// 动态读取 origin 并校验白名单
$origin = $request->header('origin');
$whitelist = config('cors.allow_origins', ['http://localhost:3000', 'https://admin.example.com']);
if ($origin && in_array($origin, $whitelist)) {
$response->header('Access-Control-Allow-Origin', $origin);
$response->header('Access-Control-Allow-Credentials', 'true');
}
// 统一设置公共头
$response->header([
'Access-Control-Allow-Methods' => 'GET,POST,PUT,DELETE,OPTIONS',
'Access-Control-Allow-Headers' => 'Authorization,Content-Type,X-Requested-With,X-Token',
 ]);
return $response;
}
}
注册中间件到全局首位
打开 app/middleware.php,确保 Cors 中间件排在数组最前面:
return [
app\middleware\Cors::class, // 必须第一行
// \think\middleware\SessionInit::class,
// \think\middleware\Auth::class,
];
配置白名单(可选,更规范)
在 config/cors.php 中定义允许域名:
return [
'allow_origins' => [
'http://localhost:3000',
'https://admin.example.com',
'https://shop.example.com'
],
];
关键避坑点
- 不要在中间件里写 header() 函数——TP6 响应走 PSR-7 对象,手动调用会被覆盖
- 带 credentials: 'include' 时,Access-Control-Allow-Origin 不能是 *,必须动态匹配 origin
- Nginx 或 Apache 若已加 CORS 头,请关闭,否则出现 "multiple values" 报错
- OPTIONS 请求必须返回 204 状态码 + 空响应体,不能返回 200 或带 JSON
- 前端发了 X-Token 或 Authorization,Access-Control-Allow-Headers 就必须显式包含它们











