CI4 的 set404Override() 必须指向真实控制器方法而非闭包,因闭包中 redirect() 无法被框架捕获导致静默失败;应配置如 'Home::index' 并确保方法返回有效响应,否则回退默认 404。

CI4 的 404 页面不能靠覆盖视图文件或写个闭包就搞定,set404Override() 必须指向一个真实可执行的控制器方法,否则重定向会静默失败。
为什么 set404Override() 里直接写 redirect()->to() 不生效
闭包在路由失败后执行,此时响应对象已部分初始化,redirect() 返回的是 Response 实例,但没被框架捕获或发送。结果就是:HTTP 状态仍是 404,浏览器看到默认 404.php,而你的重定向被丢弃。
- 闭包中调用
exit或die属于危险操作,可能中断框架生命周期 -
base_url()若未在app/Config/App.php中正确配置(比如留空或带尾部斜杠),重定向地址会出错 - 该方式无法复用中间件、过滤器或 CSRF 验证逻辑,不适合需要保持会话上下文的跳转
正确注册 404 处理器:用控制器方法代替闭包
在 app/Config/Routes.php 中调用 set404Override(),传入形如 'Home::index' 的字符串,而非匿名函数:
service('routes')->set404Override('Home::index');
确保对应控制器存在且方法返回有效响应:
-
App\Controllers\Home类必须定义index()方法 - 方法内推荐用
return redirect()->to(base_url());实现跳转 - 若想保留原 URL 显示首页内容,改用
return view('home/index');,但注意这不改变 HTTP 状态码(仍是 404)
别漏掉环境与状态码的隐性影响
CI4 在 production 环境下默认屏蔽 PHP 错误,但 404 是路由层行为,不受 display_errors 控制。真正容易被忽略的是状态码语义:
- 用
redirect()跳转后,响应状态码自动变为 302(或 307),不再是 404 —— 这对 SEO 是预期行为 - 如果业务上必须返回 404 状态(例如 API 接口),就不能跳转,而应手动渲染自定义视图并显式设置状态:
return view('errors/html/my_404')->setStatusCode(404); -
app/Views/errors/html/404.php文件仅在未调用set404Override()时才生效,改它没用
最常踩的坑是把 set404Override() 放错位置(比如写在某个路由组里),或者控制器方法没加 return 导致返回 null —— 框架会 fallback 到默认 404,整个配置就白设了。


















