CodeIgniter 4 中最稳妥的响应头设置方式是 $this->response->setHeader(),它由框架统一管理发送时机,避免“headers already sent”错误;禁用原生 header() 和已弃用的 $this->output->set_header();跨域需在过滤器中动态处理 Origin 并配全 CORS 头。

直接用 $this->response->setHeader() 最稳妥
CodeIgniter 4 的 Response 类是官方推荐、且唯一能保证响应头不被提前发送的机制。它内部做了缓冲和时机控制,避免了 header() 调用过早导致的 “headers already sent” 错误。不要在控制器里写原生 header("Access-Control-Allow-Origin: ..."),尤其别在 echo 或 print 后调用。
常见错误现象:Warning: Cannot modify header information - headers already sent,本质是输出已冲刷到缓冲区,PHP 不允许再改响应头。
-
$this->response->setHeader('Content-Type', 'application/json; charset=utf-8')—— 推荐用于 JSON 接口 -
$this->response->setHeader('X-App-Version', 'v2.1.0')—— 自定义头,无兼容性风险 -
$this->response->setHeader('Cache-Control', 'no-cache, no-store')—— 多值头建议用appendHeader()补充,比如后续加must-revalidate
为什么不能只在控制器方法里用 $this->output->set_header()
$this->output->set_header() 是 CI3 的遗留方式,在 CI4 中虽仍可用,但已被标记为“软弃用”,且不参与响应生命周期管理。它的设置会被 Response 实例覆盖,尤其在启用中间件或过滤器后行为不可靠。
更关键的是:它对 OPTIONS 预检请求完全无效——因为预检请求根本不会进入你的控制器方法,也就不会执行这行代码。
- CI4 中
$this->output主要用于兼容旧项目,新代码应统一走$this->response - 若你同时用了 CORS 过滤器(必须的),
$this->output->set_header()设置的头可能被过滤器覆盖或忽略 - 调试时用浏览器 Network 面板看响应头,如果看到重复或缺失的头,大概率是混用了两种机制
需要动态设置 Origin 时,必须结合过滤器处理
跨域场景下,Access-Control-Allow-Origin 不能简单写死成 *,尤其当请求带 credentials(如 Cookie)时,* 会直接被浏览器拒绝。此时必须根据请求中的 Origin 请求头做白名单校验,并动态回写。
这个逻辑**必须放在过滤器(Filter)里**,因为只有过滤器能在路由分发前、甚至 OPTIONS 请求阶段就介入。
- 在
app/Config/Filters.php中注册自定义过滤器,比如CorsFilter::class - 过滤器中用
$request->getHeaderLine('Origin')获取来源,比对白名单数组 - 匹配成功后调用
$response->setHeader('Access-Control-Allow-Origin', $origin)并设Access-Control-Allow-Credentials: true - 务必同时设置
Access-Control-Allow-Methods和Access-Control-Allow-Headers,否则预检失败
JSON 响应优先用 setJSON(),别手动拼 json_encode() + set_content_type()
$this->response->setJSON($data) 不仅自动设 Content-Type: application/json,还做了 UTF-8 编码检测、空数组/对象处理、以及 JSON 错误兜底(返回 500 并记录日志)。手动组合容易漏掉 JSON_UNESCAPED_UNICODE 导致中文乱码,或忘记 JSON_INVALID_UTF8_IGNORE 导致响应中断。
- 传入关联数组、对象、甚至 null 都能正确处理
- 底层等价于
json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) - 如果需额外控制 JSON 选项(如格式化缩进),先用
json_encode()得到字符串,再用setBody()+setHeader()
最易被忽略的一点:所有响应头设置都必须在 return 或 exit 前完成。CI4 的 Response 是延迟发送的,但一旦控制器方法返回,框架就会调用 send();之后再调用 setHeader() 就彻底失效了。


















