thinkphp二级域名跨域失败本质是凭据模式下access-control-allow-origin不能为*,必须动态匹配白名单中的origin;需在cors中间件中校验http_origin、设置allow-credentials为true、提前响应options请求,并确保中间件全局前置注册。

ThinkPHP 配置二级域名后跨域请求被拒,本质不是“二级域名特殊”,而是跨域策略中 Access-Control-Allow-Origin 与 Access-Control-Allow-Credentials: true 的组合触发了浏览器硬性限制——只要带凭据(如 Cookie、Authorization),Origin 就不能用 *,必须精确匹配当前请求头中的 Origin 值。而二级域名(如 api.example.com 和 admin.example.com)属于不同源,若后端静态配死一个域名或漏判 Origin,就会失败。
下面直击关键点,分场景说明怎么配才真正生效:
确认是否启用了凭据(withCredentials)
前端 fetch 或 axios 是否设置了 credentials: 'include'?jQuery 是否加了 xhrFields: { withCredentials: true }?
如果没开,那 `Access-Control-Allow-Origin: *` 是允许的;
一旦开了,后端就必须:
- 不写 `*`,改用动态获取并校验 `$_SERVER['HTTP_ORIGIN']`
- 白名单里必须包含 `admin.example.com`、`app.example.com` 等所有合法二级域名
- 同时设置 `Access-Control-Allow-Credentials: true`
用中间件动态匹配 Origin(推荐)
别在中间件里写死 `'Access-Control-Allow-Origin' => '*'`,改用白名单+判断逻辑:
在 app/middleware/Cors.php 的 handle 方法中:
- 先定义允许的二级域名数组:`$allowedOrigins = ['https://admin.example.com', 'https://app.example.com', 'http://localhost:3000'];`
- 获取请求头 Origin:
$origin = $request->header('origin'); - 检查是否在白名单内:
if ($origin && in_array($origin, $allowedOrigins)) { $response->header('Access-Control-Allow-Origin', $origin); } - 务必同步设置:
$response->header('Access-Control-Allow-Credentials', 'true'); - OPTIONS 请求要提前返回:
if ($request->isOptions()) { return response('', 204); }
中间件注册位置和顺序不能错
CORS 中间件必须是全局中间件且排在最前面,路径:app/middleware.php:
- 写成完整命名空间:
\think\middleware\Cors::class(不是字符串 'cors') - 放在数组第 1 或第 2 位,确保它在 Session、Auth、JWT 等中间件之前执行、之后结束
- 如果用了多应用模式(如 admin/、api/ 子目录),要分别在对应应用的
middleware.php中配置
绕过中间件的兜底方案(控制器层)
当需要更细粒度控制(比如某些接口允许凭据、某些不允许),可在基础控制器的 initialize() 方法里手动设头:
- 开头判断:
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { ... return response('', 204); } - 动态取 Origin 并校验,只对白名单域名写入响应头
- 注意:不能在业务逻辑之后再 set header,否则可能被覆盖或报 headers already sent
不复杂但容易忽略。核心就三点:凭据开启时 Origin 必须精确、中间件必须跑在最外层、OPTIONS 必须被正确拦截返回 204。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











