很多人在宝塔里把 admin.example.com、api.example.com 都加进站点后,发现 ThinkPHP5 依然不能按模块分流,最后不是落到默认站点,就是直接 404。问题通常不在某一个单点,而是 TP5 域名路由、宝塔站点绑定、DNS、HTTPS 和运行目录需要同时配齐。
这篇文章按实际排查顺序来讲清楚:先确认 ThinkPHP5 是否开启域名部署,再检查宝塔和 DNS 有没有把请求正确送到站点,最后核对证书、运行目录和伪静态。看完后,你可以快速判断故障究竟出在框架层、Nginx 层,还是域名与证书层。
先确认 ThinkPHP5 是否开启域名部署
ThinkPHP5 要实现二级域名到模块的映射,前提是开启 url_domain_deploy。如果这个开关没打开,TP5 不会根据请求的 Host 头去做域名路由,宝塔就算已经收到了子域名请求,框架层也识别不了。
先到 application/config.php 检查这一项:
'url_domain_deploy' => true,
接着在 application/route.php 中配置 __domain__ 规则,例如:
return [ '__domain__' => [ 'admin' => 'admin', 'api' => 'api', 'shop'=> 'shop', ], ];
这里左侧的 admin、api、shop,表示请求使用的子域名前缀,不包含主域名部分;右侧的值则是 ThinkPHP5 要进入的模块名。两边可以不相同,但右侧对应的模块目录必须真实存在,否则请求到了框架层也还是会报错或空白。
这一层经常被忽略。很多人以为子域名能访问站点,就等于模块路由也已经生效,实际上两者是两回事。Nginx 负责把请求送进来,TP5 再决定这个请求该进入哪个模块,少了其中任意一步都不行。
宝塔域名绑定和 DNS 解析必须一一对应
宝塔不会自动把 admin.example.com 视为 example.com 的附属别名,所以每个需要访问的域名都要在站点的「域名管理」里单独添加。常见情况至少包括:
example.comwww.example.comadmin.example.comapi.example.com
添加时要注意格式:每行一个域名,不带协议,不带空格,也不要用逗号拼在一起。只要漏掉一个,访问时就可能落到默认站点,或者直接返回 404。
站点里加完并不代表外部访问已经通了,DNS 也必须逐个补齐。每个子域名都需要单独配置 A 记录,或者按实际情况使用 CNAME,最终都要指向服务器真实 IP。排查时可以直接用下面两个命令验证:
ping admin.example.com curl -v http://admin.example.com
ping admin.example.com 用来确认解析是否已经落到正确 IP,curl -v http://admin.example.com 则可以继续看请求有没有真正到达你的服务器,以及返回头是不是当前站点产生的。如果 DNS 没生效,后面的 TP5 路由配置写得再对也不会起作用。
HTTPS 证书要覆盖全部子域名
如果站点走 HTTPS,这一层不能只配主域名。很多人只给 example.com 申请了证书,结果打开 https://admin.example.com 时直接报 NET::ERR_CERT_COMMON_NAME_INVALID。这不是 TP5 问题,而是证书名称和访问域名不匹配。
Let’s Encrypt 默认只会给你在「主域名」里填写的域名签发证书,所以子域名需要单独纳入证书范围。处理方式通常有两种:
- 在宝塔 SSL 页面申请证书时,手动勾选所有子域名;如果用通配符证书
*.example.com,则需要 DNS 验证 - 如果不是通配符证书,就要上传多个证书文件,并手动修改 Nginx 配置,为每个
server_name admin.example.com的server块分别指定ssl_certificate和ssl_certificate_key
改完配置后,不要停留在“保存配置”。Nginx 只有在重载之后才会真正启用新证书,所以还要执行重载操作,或者直接运行:
nginx -t && nginx -s reload
其中 nginx -t 用来先检查配置语法,确认无误后再 nginx -s reload,这是比直接重载更稳妥的做法。
运行目录与伪静态要同时对上 TP5 入口
ThinkPHP5 的入口文件是 public/index.php,因此宝塔里的「运行目录」必须设置为 /public。如果站点根目录直接指向项目根,而不是 /public,子域名请求即使到了 Nginx,也可能因为找不到正确入口而直接 404。
这个位置可以在宝塔的站点设置 → 目录中修改。改完后,还要确认伪静态规则使用的是 ThinkPHP 对应模板。常见规则如下:
if (!-e $request_filename) {
rewrite ^(.*)$ /index.php?s=$1 last;
}
宝塔里通常可以直接选择「thinkphp」模板,避免手动抄写时出错。需要注意的是,这段规则必须位于每个子域名对应的 server 块内,不能只存在于主域名的配置里。否则主站访问正常,子域名依然可能不走 TP5 的入口文件。
这也是实际环境里很常见的错位问题:域名绑定已经正确,证书也没有问题,但运行目录不是 /public,或者伪静态只写在主站配置里,最终现象仍然是子域名打不开、404,或者请求没有进入框架。
为什么子域名能打开,模块路由却还是不生效
最容易混淆的一点是:子域名生效,不等于 ThinkPHP5 的模块域名路由生效。
如果 Nginx 已经把 admin.example.com 正确转发到站点,但你仍然打不开目标模块,通常继续看这三个地方:
url_domain_deploy是否真的开启application/route.php里是否配置了__domain__- 目标模块目录是否实际存在,且名称与映射关系一致
只要其中任何一项缺失,请求虽然已经进入站点,但框架层仍然无法完成模块分流。表现出来可能是空白页、报错,或者始终落到默认模块。这个时候继续在宝塔里反复改域名绑定,通常解决不了问题。
更直接的判断方式是按顺序拆层检查:先看 DNS 是否指向服务器,再看宝塔是否绑定了对应子域名,再看 HTTPS 是否覆盖当前访问域名,最后才检查 TP5 的 url_domain_deploy 和 __domain__。这样排查效率最高,也能避免把框架问题误判成面板配置问题。















