CI4自定义404必须用set404Override()指向控制器方法并返回ResponseInterface实例;需在Routes.php中紧接$routes = service('routes')后调用,且控制器方法必须显式return响应对象。

CI4 的自定义 404 页面不能靠改 404.php 文件或写个闭包就生效,必须用 set404Override() 指向一个真实控制器方法,并确保它返回 ResponseInterface 实例——否则你看到的还是白屏或默认错误页。
为什么 set404Override() 总是不生效
最常见原因是调用顺序和返回值问题。它必须在 app/Config/Routes.php 中紧贴 $routes = service('routes'); 后立即执行,不能夹在其他路由定义中间;否则框架已锁定匹配逻辑,覆盖注册无效。
- ❌ 错误写法:
$routes->get('/', 'Home::index'); $routes->set404Override('Home::index'); - ✅ 正确写法:
$routes = service('routes'); $routes->set404Override('Home::index'); $routes->get('/', 'Home::index'); - 控制器方法里必须
return响应对象,只写redirect()->to(base_url())而不加return,PHP 返回null,框架直接 fallback 到内置404.php -
base_url()必须在app/Config/App.php中正确配置,留空或末尾多斜杠会导致跳转地址出错
用控制器方法实现重定向(推荐)
闭包在 CI4 中对 redirect() 支持不稳定,容易静默失败。把逻辑交给控制器最可靠。
- 在
app/Config/Routes.php中写:$routes->set404Override('App\Controllers\Home::index'); - 确保
App\Controllers\Home类存在,且index()方法显式返回响应:
public function index()
{
return redirect()->to(base_url());
// 或:return view('errors/html/my_404')->setStatusCode(404);
}
- 若需保持原 URL 显示首页内容(不跳转),用
return view('home/index');,但注意 HTTP 状态码仍是 404 —— 这对 SEO 不友好,仅适合内部页面兜底 - 不要在该方法中调用
exit或die,会中断框架生命周期
Nginx 配置导致自定义 404 不显示
即使 PHP 层全配对,Nginx 也可能提前拦截并返回自己的 404,完全绕过 CI。
- 检查站点配置中是否有
location / { try_files $uri $uri/ /index.php?$query_string; }—— 缺少$query_string会导致 GET 参数丢失,CI 路由解析失败,进而触发默认 404 - 禁用
fastcgi_intercept_errors on;,或确保后端 PHP 显式返回 404 状态码(如http_response_code(404)),否则 Nginx 不会启用error_page 404 - 切勿在
location ~ \.php$块里配error_page 404,这会让请求还没进 PHP 就被 Nginx 截断
状态码语义不能忽略
用 redirect() 跳转后,状态码自动变成 302(或 307),不再是 404 —— 这是正常且推荐的行为;但如果业务要求必须返回 404(比如 API 接口),就不能跳转,而要手动渲染视图并设码:
return view('errors/html/my_404')->setStatusCode(404);
另外,app/Views/errors/html/404.php 只有在没调用 set404Override() 时才生效,改它没用。真正容易被忽略的是:控制器方法没加 return、set404Override() 放错位置、Nginx 未透传查询参数 —— 这三处任一出错,整个自定义流程就断了。


















