ThinkPHP子域名伪静态需服务器层正确转发请求至index.php,Apache通过统一DocumentRoot和.htaccess规则实现,Nginx需独立server块配root与try_files;框架内须开启url_domain_deploy并用Route::domain()按域名分组定义路由。

ThinkPHP 本身不直接处理子域名路由,伪静态规则生效的前提是:Web 服务器(Apache/Nginx)先把子域名的请求正确转发到 ThinkPHP 入口文件(index.php),之后框架才基于路由配置做路径解析。子域名伪静态的关键不在 ThinkPHP 配置里,而在服务器层是否把 admin.example.com 或 api.example.com 的所有请求都交给了同一个或对应的 index.php。
Apache 下子域名共用同一套伪静态规则
适用于多个子域名共享同一套 ThinkPHP 应用(如主站 + 后台子域共用一个项目目录):
- 确保每个子域名的虚拟主机配置中,
DocumentRoot指向同一项目根目录(含index.php) -
.htaccess文件内容无需为子域名单独改写,保持通用规则即可:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
</IfModule>
但要注意:RewriteBase 不要硬写成 / 或具体路径——如果子域名对应不同子目录(如 admin.example.com 实际映射到 /public/admin/),则需在对应虚拟主机内显式设置 RewriteBase /admin/,否则重写会出错。
Nginx 子域名 location 配置必须匹配 root 和 try_files
常见错误是子域名 server 块里漏了 try_files,或 root 指向错误,导致 404 或直接下载 index.php:
立即学习“PHP免费学习笔记(深入)”;
- 每个子域名应有独立的
server块,server_name明确写死(如admin.example.com) -
root必须指向含index.php的目录(通常是项目根目录或public目录) - 务必用
try_files $uri $uri/ /index.php?$query_string;,而不是旧式if (!-e $request_filename)——Nginx 官方明确不推荐在location中使用if
正确示例(子域名入口统一走 index.php):
server {
listen 80;
server_name admin.example.com;
root /var/www/myapp/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
}
ThinkPHP 内部需识别子域名并分发路由
服务器把请求交给 index.php 后,ThinkPHP 才开始工作。此时若想让 admin.example.com/user/list 走后台模块,而 www.example.com/user/list 走前台模块,必须在路由层区分:
- 开启域名绑定路由:
'url_domain_deploy' => true(TP5.1+ 在app.php中配置) - 在
route.php中按子域名定义路由组:
Route::domain('admin.example.com', function () {
Route::rule('user/:id', 'admin/user/read');
Route::rule('user', 'admin/user/index');
});
Route::domain('api.example.com', function () {
Route::rule('v1/:controller/:action', 'api/v1.:controller/:action');
});
注意:Route::domain() 的第一个参数是完整域名(含协议无关),且必须与 Nginx/Apache 实际接收的 Host 头完全一致(大小写不敏感,但建议小写);如果用了泛域名(如 *.example.com),需配合 Route::pattern() 或中间件做动态解析。
子域名伪静态下 PATH_INFO 和 QUERY_STRING 的兼容性
某些 Nginx 配置会丢掉原始 PATH_INFO,导致 ThinkPHP 无法解析 index.php/user/list 这类路径:
- 确认
fastcgi_param PATH_INFO $fastcgi_path_info;已存在(TP 默认依赖它) - 避免在
location ~ \.php$块中重复写fastcgi_split_path_info,除非你明确需要自定义分割逻辑 - 更稳妥的做法是:在
app.php中强制关闭 PATH_INFO 模式,改用兼容性更好的URL_MODEL2(即index.php?s=xxx形式),然后在 Nginx 的try_files行末尾改成/index.php?s=$uri&$query_string
这个细节容易被忽略——当子域名 + 伪静态 + Nginx 组合时,PATH_INFO 丢失往往表现为所有路由都 fallback 到首页或 404,但日志里看不到明显报错。



















