codeigniter 4 必须用 filter 处理跨域,因 options 预检请求在路由层即被拦截,不进入控制器,故控制器内设 header 无效;需在 corsfilter::before() 中设置响应头并显式返回 204 状态码。

CodeIgniter 4 必须用 Filter 处理跨域,否则前端发 OPTIONS 预检请求时直接卡死——因为预检根本不会进控制器,header() 或 $this->output->set_header() 全部无效。
为什么在控制器里加 header("Access-Control-Allow-Origin: *") 没用
浏览器发起跨域请求前,会先发一个 OPTIONS 请求做预检。CI4 路由层在匹配到控制器前就拦截并响应这个请求,压根不执行你的控制器代码。所以你在 UsersController::index() 里写的任何 header 都不会运行。
常见现象:Failed to load http://api.example.com/v1/users: Response to preflight request doesn't pass access control check,服务端日志里甚至没这条请求记录。
- 只在控制器里设头 → 对
GET/POST有效,但OPTIONS仍失败 - 用
Base_Controller::__construct()统一加头 → 同样绕不过预检阶段 - 依赖
after()钩子 → 响应已生成,头无法再写入
怎么写一个真正生效的 CorsFilter
过滤器必须在 before() 阶段完成响应,并显式处理 OPTIONS 方法,不能只设头就完事。
在 app/Filters/CorsFilter.php 中:
public function before(RequestInterface $request, $arguments = null)
{
$response = service('response');
// 允许的 Origin(生产环境别用 *)
$origin = $request->getHeaderLine('Origin');
$allowedOrigins = ['https://your-frontend.com', 'http://localhost:5173'];
if (in_array($origin, $allowedOrigins)) {
$response->setHeader('Access-Control-Allow-Origin', $origin);
$response->setHeader('Access-Control-Allow-Credentials', 'true');
}
$response->setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
$response->setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With');
$response->setHeader('Access-Control-Max-Age', '86400');
// 关键:必须拦截并响应 OPTIONS
if ($request->getMethod() === 'OPTIONS') {
return $response->setStatusCode(204)->setBody('');
}
}
-
return $response是必须的,否则流程继续往下走,可能被后续逻辑覆盖 - 不要用
http_response_code(204)+exit(),CI4 的响应生命周期要走 service 流程 -
Access-Control-Allow-Credentials: true和Access-Control-Allow-Origin: *不能共存,会触发浏览器拒绝
Nginx 层也可能悄悄拦截 OPTIONS
即使 PHP 层配置全对,Nginx 可能早于 PHP 就把 OPTIONS 拦了,返回 405 Not Allowed。前端报错一模一样,但你查 PHP 日志完全没痕迹。
检查方式:curl -X OPTIONS -I http://your-api.com/api/users,看状态码是不是 405。
- 在 Nginx
location块里加:if ($request_method = 'OPTIONS') { add_header Access-Control-Allow-Origin "*"; add_header Access-Control-Allow-Methods "GET, POST, OPTIONS, PUT, DELETE"; add_header Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With"; add_header Access-Control-Allow-Credentials "true"; add_header Access-Control-Max-Age "86400"; add_header Content-Length "0"; add_header Content-Type "text/plain; charset=utf-8"; return 204; } - 避免在
fastcgi_pass上方写limit_except,它默认禁用OPTIONS - 确认
mod_headers已启用(Apache 场景下)
最易被忽略的一点:OPTIONS 响应必须带完整 CORS 头,且不能有 body;而很多教程只写了 setStatusCode(204) 却忘了 setHeader() 调用要在 return 前完成——顺序错了,头就丢了。











