ThinkPHP5子域名绑定多模块本质是通过HTTP_HOST切换模块入口,需在index.php中用Route::bind()手动绑定模块名,并开启url_domain_deploy=>true;Route::domain()在TP5中仅用于同模块内路由分组,不支持跨模块绑定。

ThinkPHP5 子域名绑定多模块,本质是「用 Host 头切换当前请求的模块入口」,不是靠路由规则转发,更不是复制多套代码。关键动作在入口文件 index.php 中手动调用 \think\Route::bind() 和 \think\App::route(),配合 url_domain_deploy => true 开启域名识别。
必须开启 url_domain_deploy 才能识别子域名
该配置是整个机制的前提,不开启则框架完全忽略 HTTP_HOST,所有子域名请求都走默认模块。在 config/app.php 中确认已设置:
'url_domain_deploy' => true,
注意:APP_SUB_DOMAIN_DEPLOY 是 TP3/TP5.0 旧参数,TP5.1+ 已废弃,写它没用。
入口文件 index.php 中手动绑定模块
TP5 不像 TP6 那样支持 domain_bind 配置或 Route::domain() 自动加载应用,必须在入口处根据 $_SERVER['HTTP_HOST'] 做判断并绑定模块名。常见错误是把逻辑写在路由文件或控制器里——太晚了,模块上下文早已确定。
立即学习“PHP免费学习笔记(深入)”;
- 绑定前需确保
APP_PATH指向正确,且各模块目录(如app/home/、app/admin/)结构完整 - 模块名必须与目录名一致,例如
app/admin/对应模块名'admin' -
\think\Route::bind()必须在\think\App::run()之前调用,且不能晚于自动加载器引入 - 若某子域名要关闭路由(如纯 API 场景),可通过
\think\App::route(false)控制
典型写法示例(放在 public/index.php 中):
switch($_SERVER['HTTP_HOST']) {
case 'admin.example.com':
\think\Route::bind('admin');
\think\App::route(true);
break;
case 'api.example.com':
\think\Route::bind('api');
\think\App::route(false); // 关闭路由,直通控制器
break;
default:
\think\Route::bind('home');
\think\App::route(true);
}
Route::domain() 在 TP5 中不适用于模块级绑定
很多人误以为在 route.php 里写 Route::domain('admin', 'admin') 就能切换模块——这是 TP6 的用法,TP5 不支持该语法,会直接报错 Call to undefined method think\Route::domain() 或 404。
TP5 的 Route::domain() 仅用于「在已绑定模块内,为子域名定义一组独立路由规则」,比如:
Route::domain('m', function () {
Route::get('login', 'mobile/Login/index');
});
但它不会改变模块上下文,控制器仍从当前绑定模块(如 home)下查找,不是你想要的「m.example.com → app/mobile/」效果。
Nginx/Apache 必须透传 Host 头,本地开发注意端口
Web 服务器若修改或丢弃 Host 头,$_SERVER['HTTP_HOST'] 就拿不到真实值,绑定必然失效。Nginx 示例配置关键项:
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://127.0.0.1:8000;
}
本地开发时尤其容易踩坑:
- 浏览器访问
http://admin.test:8080,但代码中只写了case 'admin.test':→ 不匹配 - 正确做法是写全
case 'admin.test:8080':,或统一用parse_url($_SERVER['HTTP_HOST'], PHP_URL_HOST)提取主机名 - 使用
localhost无法设 cookie domain,测试多子域登录务必换真实二级域名(如dev.example.com)
真正麻烦的从来不是写几行绑定逻辑,而是 Host 头在层层代理中被篡改、本地端口不一致、cookie domain 没配点号、以及误把 TP6 文档当 TP5 用——这些地方一漏,整个子域名就静默 fallback 到首页。



















