服务器与大带宽专家 · 持牌IDC/CDN/ISP服务商
简米科技官网JIANMI TECH
资讯 2026-11-12 简米科技 5,574 字 13 分钟阅读

php如何实现域名指向子目录的目录转发?,php域名转发子目录配置方法

导读用PHP实现域名指向子目录的转发,核心是在服务器配置层完成域名与目录的绑定映射,再配合PHP脚本做动态跳转兜底,即可稳定实现多个域名各自访问对应子目录的效果,实际开发中,不少朋友在宝塔面板或1Panel里添加站点时,总想把www.a.com和www.b.com分别指向/www/wwwroot/site_a和/w……

用PHP实现域名指向子目录的转发,核心是在服务器配置层完成域名与目录的绑定映射,再配合PHP脚本做动态跳转兜底,即可稳定实现多个域名各自访问对应子目录的效果。

实际开发中,不少朋友在宝塔面板或1Panel里添加站点时,总想把www.a.com和www.b.com分别指向/www/wwwroot/site_a和/www/wwwroot/site_b,却卡在配置这一步,其实解决思路不复杂:优先用Web服务器(Apache/Nginx)的虚拟主机配置,把根目录直接指向子目录;如果服务器权限受限,再用PHP脚本做转发,下面按场景拆开讲。

PHP域名指向子目录的两种核心实现路径

基于Apache的.htaccess转发配置

Apache是虚拟主机行业使用最广的Web服务器,很多老牌IDC商(如简米云虚拟主机、西部数码)默认支持.htaccess,你只需要在网站根目录放一个.htaccess文件,写入转发规则即可。

RewriteEngine On
RewriteCond %{HTTP_HOST} ^(www\.)?site_a\.com$ [NC]
RewriteCond %{REQUEST_URI} !^/site_a/
RewriteRule ^(.)$ /site_a/$1 [L]
RewriteCond %{HTTP_HOST} ^(www\.)?site_b\.com$ [NC]
RewriteCond %{REQUEST_URI} !^/site_b/
RewriteRule ^(.)$ /site_b/$1 [L]

这段规则的含义是:当用户访问site_a.com时,URL路径悄悄映射到/site_a/目录下,但浏览器地址栏不变,你要注意的点有三个:

  • [L]标志表示匹配后立即停止,避免后续规则串扰
  • 第二个RewriteCond防止已重写的请求陷入死循环
  • 目录名必须与物理路径大小写一致

在宝塔面板里的操作路径:网站 → 对应站点 → 配置文件 → 开启伪静态 → 选择ThinkPHP或自定义 → 粘贴上述代码,保存后PHP解析会自动带上子目录前缀。

基于Nginx的fastcgi分发方案

Nginx的用户比例逐年攀升,尤其在新服务器上几乎成了标配,Nginx本身不支持.htaccess,但配置反而更直观,在server块里修改root和fastcgi_param即可。

server {
    listen 80;
    server_name site_a.com www.site_a.com;
    root /www/wwwroot/main/site_a;
    index index.php index.html;
    location ~ \.php$ {
        fastcgi_pass unix:/tmp/php-cgi-00.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

按这个写法,每个域名一个独立的server块,物理目录完全隔离,PHP脚本看到的就是自己的目录路径,不需要在代码里做任何跳转判断。1Panel面板中配置域名指向子目录更简单:面板 → 网站 → 创建站点 → 填写域名 → 目录选择“子目录绑定” → 直接填相对路径(如sayes),面板会自动生成Nginx配置。

有些朋友遇到访问site_a.com/时页面正常,但访问site_a.com/login时404,这种情况通常是路径重写没带上子目录前缀,Nginx里需要追加一条try_files $uri $uri/ /index.php?$query_string;,同时fastcgi_param改成$document_root$fastcgi_script_name而不是$request_filename。

纯PHP脚本层的目录跳转实现

getenv获取域名并做301重定向

如果服务器是虚拟主机且不支持改站点根目录,那就在入口文件index.php里用PHP判断域名并跳转,这个方法在php域名指向子目录怎么实现的搜索结果里最常出现,因为门槛最低。

<?php
$domain = $_SERVER['HTTP_HOST'];
$map = [
    'www.site_a.com' => '/sub/a/',
    'www.site_b.com' => '/sub/b/',
];
foreach ($map as $key => $dir) {
    if ($domain === $key) {
        header('Location: ' . $dir, true, 301);
        exit;
    }
}
header('Location: /default/', true, 301);

用这种方式要注意几个坑:$_SERVER['HTTP_HOST']可能带端口号,最好用parse_url处理一下;301是永久重定向,浏览器会缓存,调试阶段建议先改成302,如果站点本身用了伪静态路由,这种原始跳转会丢掉URL参数,需要拼接$_SERVER['REQUEST_URI']保留完整路径。

透明内部转发与URL重写

301跳转会让地址栏变化,有时候客户不接受,想做到地址栏始终显示域名、内容来自子目录,就得用内部转发:

<?php
$host = $_SERVER['HTTP_HOST'];
$baseDir = '';
if (strpos($host, 'site_a.com') !== false) {
    $baseDir = '/site_a';
} elseif (strpos($host, 'site_b.com') !== false) {
    $baseDir = '/site_b';
}
if ($baseDir) {
    $uri = $_SERVER['REQUEST_URI'];
    // 防止重复拼接
    if (strpos($uri, $baseDir) !== 0) {
        $_SERVER['REQUEST_URI'] = $baseDir . $uri;
        $_SERVER['SCRIPT_NAME'] = $baseDir . '/index.php';
    }
    require __DIR__ . $baseDir . '/index.php';
} else {
    http_response_code(404);
}

这种方案更适合小型项目或快速验证原型,它的本质是手动改写了PHP超全局变量,让框架误以为请求本来就指向子目录。但要注意:require后会直接执行子目录的index.php,如果子目录代码里再依赖__DIR__定义路径,需要确保相对路径解析正确。

PHP框架路由层面的目录绑定

ThinkPHP和Laravel的多应用映射

如果你用的是ThinkPHP 6或Laravel,处理域名绑定子目录有一种更优雅的做法:在路由配置里建立域名与模块的映射关系。

ThinkPHP官方文档里提到,多应用模式支持通过app_map实现域名绑定:

// config/app.php
'app_map' => [
    'site_a.com' => 'shop',
    'site_b.com' => 'blog',
],

这样site_a.com下的所有请求直接落到app/shop控制器层,而不用在服务器配置文件里折腾目录。php域名绑定子目录伪静态配置也因此简化:你只要保证Nginx把所有请求交给index.php即可,剩下的路由解析全由PHP完成。

Laravel的做法是使用Route::domain():

Route::domain('{account}.site_a.com')->group(function () {
    Route::get('/', function ($account) {
        return view($account . '.index');
    });
});

中间件完成动态目录切换

需求经常是动态的用户绑定自己的域名,然后映射到指定目录,这时写死在配置文件里不现实,中间件是更优解。

public function handle($request, Closure $next)
{
    $host = $request->getHost();
    $userDomain = UserDomain::where('domain', $host)->first();
    if ($userDomain) {
        $path = $request->path();
        $newPath = $userDomain->directory . '/' . $path;
        $request->server->set('REQUEST_URI', '/' . $newPath);
        $request->server->set('SCRIPT_NAME', '/' . $userDomain->directory . '/index.php');
    }
    return $next($request);
}

用户通过域名访问子目录部署时,系统根据数据库记录把请求映射到对应租户目录,SaaS系统里常见这种做法,免去每次上新用户都要改Nginx的麻烦。

实际部署中的坑与排查思路

伪静态与子目录的冲突

很多开发者说“解析对了,目录也指向了,但子页面全部404”,十有八九是伪静态规则只写了根路径,没覆盖子目录,以Nginx为例,如果你有两个子目录,不要让它们共享同一个location /块,应该各写各的:

location /site_a/ {
    try_files $uri $uri/ /site_a/index.php?s=$uri;
}
location /site_b/ {
    try_files $uri $uri/ /site_b/index.php?s=$uri;
}

本地开发环境模拟多域名

用PHPStudy或MAMP做本地测试时,改C:\Windows\System32\drivers\etc\hosts文件,把多个域名都解析到0.0.1,Apache里开启VirtualHost也指向同一IP不同目录,这样本地和线上保持一致的目录结构,避免“本地好好的,上传到服务器就乱跳”。

HTTPS证书与目录转发的联动

开启SSL后,你要确保server块里同时监听443端口,并且证书路径正确,在武汉、杭州这些城市的企业客户里,经常出现http访问正常但https下跳转失效的情况,原因就是伪静态规则忽略了{HTTPS}参数,Apache里可以加一条:

RewriteCond %{HTTPS} !=on
RewriteRule ^(.)$ https://%{HTTP_HOST}/$1 [R=301,L]

FAQ:php域名转发常见疑问

问:php代码实现跳转和服务器重写,哪个优先级更高?站长在宝塔面板里如何选择?

答:如果服务器配置了Rewrite规则(Apache或Nginx),Web服务器会在PHP执行前完成URL重写,因此优先级高于PHP脚本跳转,宝塔面板里可以在“伪静态”功能中添加Rewrite规则,对应Nginx的try_files和Apache的.htaccess,当两者同时存在时,以服务器配置为准。

问:子目录绑定后,为什么子目录里的图片和CSS加载不出来?

答:通常是因为URL重写只处理了PHP请求,未处理静态资源路径,排查时先看浏览器控制台请求的是哪个地址,如果是相对路径(如css/style.css),则确认服务器配置中location块是否包含静态文件后缀的匹配规则;如果是绝对路径(如/css/style.css),则需在Rewrite规则中追加条件,让静态请求直接映射到子目录对应路径,无需PHP介入。

分享本文
本文为 简米科技官网 原创,已由运维技术专家审核。转载请注明来源:原文链接
售前咨询 服务热线 售后 邮箱