ThinkPHP路由失效的根本原因是Nginx未将非静态请求转发至index.php;必须配置location /块配合try_files $uri $uri/ /index.php?s=$uri&$args,确保所有非真实文件请求兜底重写,且location ~ .php$须保留并置于location /之前。

ThinkPHP 路由失效,Nginx 返回 404 的根本原因
不是 ThinkPHP 配置错了,是 Nginx 根本没把请求交给 index.php 处理——location 块没兜住除静态资源外的所有请求。
ThinkPHP 的伪静态依赖 Nginx 把所有非真实文件/目录的请求,全部重写到 index.php 入口。如果 location ~ \.php$ 只匹配带 .php 后缀的 URL,那像 /user/list 这种路由就直接 404 了。
- 常见错误现象:
GET /admin/dashboard返回 404,但GET /index.php/admin/dashboard正常 - 典型误配:
location ~ \.php$单独存在,没配更宽泛的try_files或rewrite规则 - 关键点:ThinkPHP 的「伪静态」本质是 URL 重写,Nginx 层必须主动做转发,不能指望 PHP 自己“解析路径”
Nginx 必须写的 location / 块(不是可选)
ThinkPHP 要跑起来,location / 是绕不开的入口守门员。它负责判断请求是否对应真实文件,不是就甩给 index.php。
别用 rewrite ^(.*)$ /index.php?s= last; 这类老写法,容易循环或漏掉 query string;现代推荐用 try_files + fastcgi_param 组合。
立即学习“PHP免费学习笔记(深入)”;
- 标准写法(放在
server块内):location / { try_files $uri $uri/ /index.php?s=$uri&$args; } -
$uri匹配静态文件(如/static/css/app.css),$uri/匹配目录,最后兜底到/index.php?s=... - 注意
&$args—— 没它,带参数的 URL(如/user?id=123)里$_GET会为空 - 如果项目部署在子目录(如
https://example.com/shop/),/index.php要改成/shop/index.php,且s=$uri改为s=$request_uri更稳妥
为什么 location ~ \.php$ 不能删、也不能乱改
这个块不处理路由,只负责把 PHP 脚本交给 PHP-FPM 执行。但它和上面的 location / 是协作关系——前者只响应以 .php 结尾的请求,后者兜底其他所有请求并重写为 /index.php?...,再触发前者执行。
- 必须保留
location ~ \.php$,否则/index.php本身都打不开 - 常见坑:
location ~ \.php$写在location /之后,导致优先级错乱(Nginx 按顺序匹配,最长前缀优先,但正则优先级高于普通 location) - 安全建议:加
fastcgi_intercept_errors on;,避免 PHP 错误信息直接暴露 - 务必确认
fastcgi_param SCRIPT_FILENAME指向的是真实磁盘路径,例如:fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
而不是硬编码路径
调试时先看 Nginx error log,不是 ThinkPHP 日志
路由 404 时,90% 是 Nginx 没转发成功,PHP 根本没收到请求。这时候翻 ThinkPHP 的 runtime/log 是白忙活。
- 查日志命令:
tail -f /var/log/nginx/error.log - 典型线索:
open() "/var/www/html/user/list" failed (2: No such file or directory)→ 说明 Nginx 尝试找物理文件,没走try_files兜底 - 另一个线索:
FastCGI sent in stderr: "Primary script unknown"→SCRIPT_FILENAME路径拼错了,PHP-FPM 找不到index.php - 临时验证:在
location /里加return 200 "hit location /";,访问任意路径看是否返回,能快速定位是否进对了 block
真正麻烦的是 rewrite 和 try_files 混用时的隐式跳转,比如多层子目录 + PATH_INFO 模式,这时候 $request_uri 和 $uri 的差异就会咬人。别猜,先打日志。



















