多域名共用数据库配置在ThinkPHP 5中可行,但需手动按域名动态切换数据库,因database.php加载早于域名识别,APP_SUB_DOMAIN_DEPLOY不作用于数据库层;推荐用isset($_SERVER['HTTP_HOST'])安全判断并结合Env或Db::connect()实现。

多域名共用数据库配置在 ThinkPHP 5 中完全可行,但必须手动剥离域名相关逻辑,不能依赖 APP_SUB_DOMAIN_DEPLOY 或自动切换机制——它不会帮你改数据库连接。
为什么 config/database.php 不会随域名自动变化
ThinkPHP 5 的数据库配置加载发生在应用初始化早期,远早于路由和域名识别阶段。也就是说,config/database.php 被读取时,$_SERVER['HTTP_HOST'] 还没被用于路由匹配,框架也尚未决定走哪个模块或子域名。所有域名请求都共享同一份 database 配置,除非你主动干预。
- 常见错误现象:
APP_SUB_DOMAIN_DEPLOY => true开启后,仍发现 admin.example.com 和 api.example.com 查的还是同一个库,因为该配置只影响模块路由,不触碰数据库层 - 配置项
APP_SUB_DOMAIN_DEPLOY在 TP5 是有效的,但它只控制app/下模块加载路径,和database.php完全无关 - 若强行在
database.php中写$_SERVER['HTTP_HOST'],会导致 CLI 环境(如命令行执行php think)报错,因为HTTP_HOST不存在
如何安全地按域名加载不同数据库配置
推荐在 config/database.php 返回数组前做运行时判断,但需包裹保护逻辑,避免 CLI 崩溃。
- 用
isset($_SERVER['HTTP_HOST'])判断是否为 Web 请求,再提取域名特征 - 不要直接用
parse_url(),因为HTTP_HOST不含协议和 path,直接explode('.', $_SERVER['HTTP_HOST'])更轻量 - 示例:区分
admin.example.com和www.example.com,可取二级域或子域部分做 key 匹配 - 敏感参数(如密码)仍建议从
.env读取,避免硬编码;TP5 支持Env::get('DB_PASSWORD_ADMIN')(需先use think\Env;)
简短示例(放在 config/database.php 文件末尾返回前):
立即学习“PHP免费学习笔记(深入)”;
if (isset($_SERVER['HTTP_HOST'])) {
$host = $_SERVER['HTTP_HOST'];
if (strpos($host, 'admin.') === 0) {
$config['database'] = Env::get('DB_NAME_ADMIN', 'admin_db');
$config['username'] = Env::get('DB_USER_ADMIN', 'admin_user');
} elseif (strpos($host, 'api.') === 0) {
$config['database'] = Env::get('DB_NAME_API', 'api_db');
$config['username'] = Env::get('DB_USER_API', 'api_user');
}
}
Db::connect() 动态连接更适合多域名场景
比起修改全局配置,更推荐在模型或服务层用 Db::connect() 按需加载——它不污染默认连接,且能配合中间件统一注入。
- 在中间件中根据
request()->host()设置一个请求上下文变量,如think\facade\Cache::set('db_context', 'admin') - 在模型基类的
initialize()方法里读取该上下文,调用Db::connect('mysql_' . $context)并绑定到当前模型 - 注意:
Db::connect('mysql_admin')中的键名必须与config/database.php的connections数组键一致;TP5 默认没有connections结构,需手动加一层封装 - 若未预定义连接,也可传数组:
Db::connect(['type'=>'mysql', 'database'=>'admin_db', ...]),但每次请求新建连接开销大,不建议高频使用
容易忽略的部署细节
本地测试时 hosts + Apache/Nginx 虚拟主机没问题,但上线后常因 CDN、反向代理或负载均衡导致 HTTP_HOST 被覆盖或变成内网 IP。
- Nginx 反向代理下,确保 upstream 透传了
Host头:proxy_set_header Host $host; - Cloudflare 等 CDN 后,
HTTP_HOST仍是用户访问的域名,但真实 IP 变成 CF 的出口 IP,不影响域名判断 - CLI 场景(如定时任务)永远走默认库,所以跨域名业务若需命令行同步数据,必须显式指定连接名,不能依赖 host 判断



















