CORS跨域在Yii中不生效,主因是Access-Control-Allow-Credentials为true时Origin不能设为['*'],且需正确配置'as cors'位置、显式声明Origin列表及Access-Control-Request-Headers。

CORS 跨域在 Yii 中不生效,大概率是 Access-Control-Allow-Credentials 和 Origin 配置冲突导致的 —— 这不是代码写错了,而是浏览器强制限制。
Yii2 中 Cors::className() 不生效的常见原因
很多人把 'as cors' 放进 components 里,或者在 behaviors() 里写对了但没调用 parent::behaviors(),结果配置被覆盖。更隐蔽的问题是:即使配置加载了,只要 'Origin' => ['*'] 和 'Access-Control-Allow-Credentials' => true 同时存在,浏览器就直接拒绝响应,控制台报错:
Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
这个错误不是 Yii 报的,是浏览器拦截的,所以你查 Yii 日志、看 network tab 的 response headers 都可能看到头已经写了,但前端仍拿不到数据。
-
'Origin'必须明确列出(如['http://localhost:3000', 'https://myapp.com']),不能用['*']配合'Access-Control-Allow-Credentials' => true - 如果前端没发
withCredentials: true或credentials: 'include',那可以放宽为'Origin' => ['*'],但多数登录态场景都需要凭证 -
'Access-Control-Request-Headers'如果前端带了自定义头(比如X-Auth-Token),就必须显式列出来,不能只写['*'](某些 Yii 版本不支持通配)
在 config/web.php 全局启用 cors 的正确写法
把 'as cors' 放在 Application 配置顶层,和 components、modules 平级,不是它的子项:
'as cors' => [
'class' => \yii\filters\Cors::className(),
'cors' => [
'Origin' => ['http://localhost:3000', 'https://prod.myapp.com'],
'Access-Control-Request-Method' => ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
'Access-Control-Request-Headers' => ['Content-Type', 'Authorization', 'X-Requested-With'],
'Access-Control-Allow-Credentials' => true,
'Access-Control-Max-Age' => 3600,
'Access-Control-Expose-Headers' => ['X-Pagination-Current-Page', 'X-Rate-Limit-Limit'],
],
],
注意几个关键点:
-
'Origin'列表必须包含前端实际请求的完整协议+域名+端口(http://localhost:3000≠http://127.0.0.1:3000) -
'Access-Control-Request-Method'是预检请求里带的头,不是你实际接口支持的方法;它得覆盖你所有可能用到的 method,否则 OPTIONS 返回 405 - 如果你用的是 JWT 或 session cookie,且前端设置了
credentials: 'include',那'Access-Control-Allow-Credentials' => true就必须配,且'Origin'不能是['*']
Controller 层局部启用 cors 的安全写法
想只对某几个 API 开启跨域?别直接覆盖整个 behaviors(),先保留父类行为再合并:
public function behaviors(): array
{
$behaviors = parent::behaviors();
$behaviors['corsFilter'] = [
'class' => \yii\filters\Cors::className(),
'cors' => [
'Origin' => ['https://admin.myapp.com'],
'Access-Control-Request-Method' => ['POST', 'OPTIONS'],
'Access-Control-Allow-Credentials' => true,
],
];
return $behaviors;
}
这种写法容易踩的坑:
- 忘记
return $behaviors,或写成return ['corsFilter' => ...],会丢掉authenticator、rateLimiter等默认行为 - 在
yii\rest\Controller子类里,parent::behaviors()已经自带'authenticator',直接覆盖会导致鉴权失效 - 如果该 Controller 继承自自定义基类,要确认基类是否已定义
behaviors()并做了 merge
调试时最容易忽略的三个点
很多问题卡在“明明配了却没反应”,其实是因为:
- 浏览器缓存了 OPTIONS 响应(
Access-Control-Max-Age设得太大),改完配置后没清缓存或换无痕窗口测试 - 前端发的是简单请求(如 GET + Content-Type:text/plain),根本不会触发预检,所以你改
Access-Control-Request-Method没用;只有带自定义 header 或非简单 method 才走 OPTIONS - Nginx/Apache 层面拦截了 OPTIONS 请求,比如 Nginx 里没配
location / { if ($request_method = 'OPTIONS') { add_header ...; return 204; } },导致 Yii 根本收不到预检请求


















