ThinkPHP多应用伪静态丢失模块名本质是Nginx未透传路径前缀,需用正则提取应用名并设运行目录为/public、启用PATH_INFO、禁用putenv/ini_set限制。

ThinkPHP多应用模式下,跨模块访问时伪静态丢失模块名(比如访问 /admin/login 却被当成 /login 进了默认应用),本质不是路由写错了,而是请求路径没完整、准确地交到框架手里——模块前缀在Nginx转发环节就被截断或覆盖了。
确认是否真为“模块名丢失”而非应用绑定失败
先排除低级误判:
- 访问
/admin/login页面空白或报“找不到控制器”,但/index.php?s=/admin/login能正常打开 → 是伪静态规则没把/admin透传过去 - 访问
/admin/login直接进了app/index的控制器 → 是应用识别失败,可能default_app配置异常,或domain_bind写错,或入口未指向对应 public 目录 - 页面能加载,但生成的链接全是
/login没带/admin/前缀 → 是 URL 生成逻辑问题,需检查url()或自定义siteurl()函数是否主动去除了前缀
Nginx 伪静态必须捕获并透传应用前缀
宝塔默认的 ThinkPHP 规则(rewrite ^(.*)$ /index.php?s=$1 last;)只适用于单应用,它把整个路径当做一个字符串塞进 s=,完全忽略第一段是应用名这一设计前提。TP6/TP8 多应用靠 $_GET['s'] 或 PATH_INFO 的首段来分发,所以规则必须显式提取。
正确写法(粘贴到宝塔「伪静态」栏,清空原有内容):
立即学习“PHP免费学习笔记(深入)”;
if (!-e $request_filename) {
rewrite ^/([a-z0-9A-Z_\-]+)/(.*)$ /public/index.php?s=$1/$2 last;
rewrite ^/(.*)$ /public/index.php?s=$1 last;
}
- 第一行匹配带应用前缀的请求,如
/admin/login?tab=1→ 提取admin和login?tab=1,拼成s=admin/login?tab=1 - 第二行兜底,确保
/robots.txt、/、/api/v1等不带前缀的路径也能进入口,由框架自己判断 -
last不可替换为break:只有last才会重新进入 location 匹配流程,保证 PHP-FPM 正确加载/public/index.php并保留原始查询参数
配套三项配置缺一不可
光改伪静态规则,其他地方没对齐,照样 404 或白屏:
-
运行目录必须设为
/public:宝塔 → 网站 → 设置 → 网站目录 → 运行目录 → 下拉选择/public(不是手动输,也不是留空) -
PHP 禁用函数中移除
putenv和ini_set:TP6/TP8 启动时需这两个函数设置环境变量和运行时配置,禁用会导致 PATH_INFO 初始化失败 -
PHP location 块中必须含 PATH_INFO 支持:在站点配置的
location ~ \.php$内检查是否有:fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_param PATH_INFO $fastcgi_path_info;
若没有,手动添加;若已存在但被注释,取消注释
验证与调试建议
改完后务必点击「保存」→「重载 Nginx」→「重启 PHP 服务」三步全做,缺一不可。
- 临时在
public/index.php开头加一行:file_put_contents('/tmp/s_debug.log', print_r($_GET, true) . "\n" . $_SERVER['PATH_INFO'] . "\n", FILE_APPEND);,然后访问/admin/login,查看日志里s参数是否为admin/login,PATH_INFO是否非空 - 检查 Nginx 错误日志(宝塔 → 网站 → 日志 → 错误日志),出现
Primary script unknown表示SCRIPT_FILENAME拼错;出现No input file specified多因.user.ini限制或防跨站白名单未精确到/public/ - TP8 用户注意:官方已废弃传统多应用目录结构,若强行沿用
app/admin,必须确认config/app.php中'app_multi_module' => true和'auto_multi_module' => true同时开启,且每个子应用目录下有独立的config/app.php



















