CodeIgniter 4 的 show_404() 不触发自定义错误页,是因为它仅抛出 PageNotFoundException 异常,实际渲染取决于环境配置(如 APP_DEBUG=true 时强制显示调试堆栈)、异常是否进入 App\Exceptions\Handler::render() 方法、error_404.php 路径是否正确(app/Views/errors/html/),以及响应头是否已发送。

CodeIgniter 4 的 show_404() 为什么没触发自定义错误页
因为默认情况下,show_404() 只是抛出一个 PageNotFoundException 异常,最终由全局异常处理器接管——而你写的 app/Views/errors/html/error_404.php 是否生效,取决于当前环境配置和异常处理链是否被绕过。
常见错误现象:show_404() 调用后仍显示空白页、PHP 错误堆栈、或 CI 默认的“404 Page Not Found”纯文本提示。
-
APP_DEBUG = true时,CI 会强制显示调试堆栈,跳过所有自定义错误视图 - 在控制器构造函数、中间件或服务初始化阶段调用
show_404(),可能因响应已发送(headers sent)导致失败 - 未将
error_404.php放在正确路径:app/Views/errors/html/(不是views/或public/) - 重写了
BaseController::initController()却没调用parent::initController(),导致异常处理器未注册
如何让 404/500 错误真正走自定义 HTML 视图
关键不是改视图文件,而是确保异常能落到 App\Exceptions\Handler 的 render() 方法里,并且该方法明确委托给 view() 渲染错误模板。
- 确认
app/Config/Exceptions.php中$pageCacheMaxAge不为 0(否则可能缓存空响应) - 在
app/Exceptions/Handler.php的render()方法中,对PageNotFoundException和HTTPException显式返回view('errors::html/error_404') - 避免在
render()中直接echo或调用exit(),这会中断 CI 响应生命周期 - 若使用
response()->setStatusCode(404)+view()手动组合,需确保未提前调用send(),否则 headers 已发送
示例片段(app/Exceptions/Handler.php):
public function render($request, Throwable $throwable)
{
if ($throwable instanceof PageNotFoundException) {
return view('errors::html/error_404', ['exception' => $throwable]);
}
return parent::render($request, $throwable);
}
CI4 中 response()->setStatusCode() 和真实 HTTP 状态码的关系
调用 response()->setStatusCode(404) 只是设置响应对象内部状态,不等于浏览器收到 404;最终是否发出,取决于这个响应对象有没有被 CI 内核真正发送出去。
- 在控制器中调用后立即
return $this->response,才能保证状态码和内容一起发出 - 若在中间件中调用但未 return 响应,后续控制器仍会执行,可能覆盖状态码
- CLI 请求下,HTTP 状态码无意义,
setStatusCode()不会产生实际效果 - 部分 Nginx/Apache 配置会拦截 4xx/5xx 响应并替换为自己的错误页(如
error_page 404 /404.html),此时 CI 视图不会显示
调试错误响应时最容易忽略的三个点
不是代码写错,而是环境、时机、配置三者咬合不上。
-
index.php顶部的define('ENVIRONMENT', 'development')没改,导致始终走调试模式 - 在模型或服务类里抛出异常,但没在控制器中 catch,结果被全局 Handler 捕获——这时你改的控制器内错误逻辑根本没机会执行
- 使用了第三方库(比如 JWT 验证中间件)抛出非 CI 标准异常,而
Handler::render()没覆盖该类型,直接 fallback 到默认白屏
真要定位,打开 app/Config/Logger.php 把 $threshold 设为 DEBUG,看日志里异常是否被抛出、在哪一层被捕获、是否进了 render()。










