CodeIgniter 4 在 Nginx 下非首页 URL 全部 404 的根本原因是 Nginx 未正确将请求转发给 index.php,必须配置 try_files $uri $uri/ /index.php?$query_string;,root 指向 public/ 目录,并同步设置 $config['index_page'] = '' 与 $config['uri_protocol'] = 'REQUEST_URI'。

CodeIgniter 4 在 Nginx 下跑不起来、非首页 URL 全部 404,不是框架问题,是 Nginx 没把请求正确转给 index.php —— 这个环节漏了,其他配置全白搭。
nginx 配置必须用 try_files,不能靠 rewrite
很多老教程教你在 location / 里写 rewrite ^/(.*)$ /index.php?$1 last;,这在 CI4 下极易出错:Nginx 会反复重写、丢参数、甚至触发 500。CI4 的路由依赖完整的原始 URI(含 query string),rewrite 很难保全。
正确做法是用 try_files 做“存在性兜底”:
-
try_files $uri $uri/ /index.php?$query_string;—— 优先找真实文件或目录,否则交给index.php,且显式传递$query_string - 若项目入口是
public/目录(CI4 默认),root必须指向public/,且try_files中的/index.php前面**不能加路径前缀**,即写成/index.php?$query_string;,不是/public/index.php -
location ~ \.php$块里,fastcgi_param SCRIPT_FILENAME必须用$realpath_root$fastcgi_script_name,硬编码路径(如/var/www/myapp/public/index.php)会导致符号链接环境下 502
index_page 和 uri_protocol 要同步关掉
CI4 默认带 index.php,但 Nginx 配好了就该彻底去掉它。这两项不配对,URL 会错乱或 404:
-
$config['index_page'] = '';—— 清空字符串,不是null或注释掉 -
$config['uri_protocol'] = 'REQUEST_URI';—— 不要用AUTO,Nginx 下REQUEST_URI最稳;PATH_INFO在某些 PHP-FPM 配置下会截断参数 - 改完记得清缓存:
php spark cache:clear,否则旧路由规则可能还在生效
writable/ 权限不是 777,而是属主 + 执行位
报错 mkdir(): Invalid path 或 session 写入失败,90% 是因为 writable/ 目录权限或路径不对,不是数字不够大。
-
$config['sess_save_path']必须是绝对路径,推荐写法:FCPATH . 'writable/sessions'(FCPATH是 CI4 自带常量,指向public/上级) - 手动创建目录后,执行:
chown www-data:www-data /var/www/myapp/writable(用户组按你实际 Web 用户调) - 再设权限:
chmod 700 /var/www/myapp/writable—— 关键是父目录要有执行位(x),否则 PHP 进程进不去子目录;777反而可能被安全策略拒绝
启动前必须检查 .env 和 php spark serve 的区别
本地开发用 php spark serve 是 PHP 内置服务器,和 Nginx 完全无关;上线部署时,这个命令没用,且可能掩盖 Nginx 配置错误。
- 确认
.env已复制并启用:CI_ENVIRONMENT = development(调试阶段),app.baseURL = 'http://your-domain.com'(不能留空或写localhost) - Nginx 启动后,用
nginx -t验证语法,再systemctl reload nginx,别只改配置不重载 - 如果仍 404,直接 curl 测试:
curl -I http://localhost/index.php/test看是否通——不通说明 Nginx 根本没转发到 PHP,问题还在location ~ \.php$块
真正卡住的地方,往往不是某一行配置写错,而是 root 指向了项目根目录却没意识到 CI4 入口在 public/,或者 try_files 少了 $query_string 导致 GET 参数消失——这两个点一旦错,整个路由链就断了,且错误表现全是 404,没有明确提示。











