Nginx 多站点配置实战:一台服务器优雅托管多个网站

前言:一台服务器,为什么要托管多个网站

个人站长手里通常不止一个站点:一个技术博客、一个资源下载站、可能还帮朋友托管一个企业官网。如果每个站都单独买一台服务器,成本高得离谱;把多个网站放在同一台 Nginx 上,才是性价比最高的方案。Nginx 通过"虚拟主机"(server block)机制,可以在同一个 IP、同一个 80/443 端口上同时服务成百上千个域名,互不干扰。本文从目录规划讲起,带你完整搭建一套多站点 Nginx 环境,并避开新手最容易踩的坑。文章里的配置都经过实际验证,你可以直接复制修改使用,建议边读边在测试机上动手操作一遍,理解会更深刻。

一、先规划目录结构

多站点管理的第一原则是"一个域名一个目录,配置和网站文件分开"。推荐的结构如下:

/var/www/
├── example.com/          # 站点A,域名即目录名
│   └── public_html/      # 网站根目录
├── blog.example.com/     # 站点B
│   └── public_html/
/etc/nginx/
├── sites-available/      # 存放所有站点的配置(可禁用的"开关")
│   ├── example.com.conf
│   └── blog.example.com.conf
└── sites-enabled/        # 软链接指向启用的站点
    ├── example.com.conf -> ../sites-available/example.com.conf
    └── blog.example.com.conf -> ../sites-available/blog.example.com.conf

Debian/Ubuntu 系的 Nginx 默认就是这种 sites-available / sites-enabled 结构:配置放在 available 里,用 ln -s 软链接到 enabled 就算启用,删掉软链接就是禁用,管理起来非常清晰。CentOS 系的 nginx.conf 默认没有这两个目录,需要自己创建,并在 nginx.conf 的 http 块里加上 include /etc/nginx/conf.d/*.conf; 或者对应路径。

二、写第一个 server block

Nginx 配置的核心就是 server 块。每个 server 块声明一组监听规则和对应的网站根目录,Nginx 根据请求的 Host 头(也就是用户访问的域名)来决定由哪个 server 块处理。一个托管 PHP 程序的站点配置长这样:

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

    root /var/www/example.com/public_html;
    index index.php index.html;

    access_log /var/log/nginx/example.com.access.log;
    error_log  /var/log/nginx/example.com.error.log;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}

几个关键点:server_name 决定这个站点响应哪个域名;root 指向站点根目录;PHP 请求通过 fastcgi 转发给 PHP-FPM 处理。第二个站点复制一份配置,改掉 server_name、root 和日志路径即可,其余完全一样。如果第二个站点是纯静态站,把 PHP 相关的 location 删掉就行。

三、启用、测试与重载

配置文件写好后,按下面的顺序操作,先测试再生效,避免语法错误把整个 Nginx 搞挂:

# 1. 创建软链接启用站点
ln -s /etc/nginx/sites-available/example.com.conf /etc/nginx/sites-enabled/

# 2. 语法检查,输出 syntax is ok 才算通过
nginx -t

# 3. 重新加载配置(平滑生效,不中断连接)
systemctl reload nginx   # 或 nginx -s reload

nginx -t 会检查所有配置文件的语法,如果有问题会明确提示出错的文件和行号。reload 是平滑重载,正在处理的请求不会中断,这是 Nginx 多站点管理里最常用的操作,改完配置第一时间 reload 而不是 restart。

四、没有匹配域名的请求怎么办:默认 server

多站点配置里最容易被忽略的安全问题:如果有人直接用 IP 访问你的服务器,或者用别的域名解析到你的 IP 再访问,请求会落在哪个站点?Nginx 的规则是"第一个 server 块"或者"listen 指令带 default_server 标记"的块。如果你不处理,别人的域名解析到你的 IP 上就能看到你某个站点的内容,还可能被用来做恶意跳转。正确的做法是单独建一个默认站点,直接拒绝这些请求:

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 444;
}

把这段配置放在最前面(或者作为第一个加载的站点),任何不认识 Host 的请求都会被直接断开连接(444 是 Nginx 特有的"直接关闭连接"状态码)。注意:这个默认 server 块必须在其他站点之前被 include,所以文件名用 00-default.conf 之类的排序靠前的名字最稳妥。

五、日志分离:每个站点一份独立的访问日志

多站点如果不做日志分离,所有访问记录都堆在同一个 access.log 里,排查某个站点的问题时要在海量记录里大海捞针。正确的做法是在每个 server 块里单独指定日志路径:

server {
    server_name example.com www.example.com;
    access_log /var/log/nginx/example.com.access.log;
    error_log  /var/log/nginx/example.com.error.log;
    # 还可以按域名单独统计
    access_log /var/log/nginx/example.com.access.log combined;
}

日志分开之后,配合 logrotate 对每个站点的日志独立轮转(比如流量大的站点日志一天一切,小站点一周一切),再用 goaccess 之类的工具对单站日志做访问统计,每个站点的流量来源、热门页面、访问者地区都清清楚楚。对个人站长来说,日志就是最真实的运营数据,分离日志是投入产出比极高的一件事。另外建议在 logrotate 配置里给 Nginx 日志加上 copytruncate 或者 postrotate 里执行 reload,避免轮转时产生前面说的"幽灵文件"占用磁盘的情况。

六、HTTPS 多站点:SNI 自动匹配证书

现在全站 HTTPS 是标配。多站点环境下每个域名都要有自己的证书,Nginx 通过 SNI(Server Name Indication)机制,在 TLS 握手阶段就根据域名自动选择对应证书,完全不需要额外配置:

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name example.com www.example.com;
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    # 其他配置与 80 端口相同...
}

# HTTP 跳转 HTTPS
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

用 certbot 申请证书时,certbot --nginx -d example.com -d www.example.com 会自动帮你改好 Nginx 配置。每个域名独立申请证书,互不影响,证书到期续期时也只影响对应站点。

七、按端口和 IP 区分站点的特殊用法

除了按域名区分,Nginx 还支持按端口或按 IP 区分站点。端口区分适合"内部工具站"场景,比如同一个 IP 上,80 端口跑正式网站,8080 端口跑一个只给自己用的管理面板,别人不知道端口号就无法访问。配置方法就是在 listen 指令里指定端口:

server {
    listen 8080;
    server_name _;
    root /var/www/internal-tool;
    # 可以加上内网 IP 限制
    allow 192.168.1.0/24;
    deny all;
}

IP 区分则适用于服务器绑定了多个 IP 的情况(比如同时有 IPv4 和 IPv6,或者买了多个 IP 做不同业务),写法是 listen 1.2.3.4:80;。不过对个人站长来说,最常用的还是域名区分,端口和 IP 区分属于进阶用法,了解即可。另外提醒一点:用端口区分时,如果网站开启了防火墙,记得放行对应端口,否则外部访问会超时。

八、多站点下的 PHP-FPM 资源分配

多站点共用同一个 PHP-FPM 服务时,要注意资源分配问题。默认情况下所有站点共用一个 FPM 进程池,某个站点流量暴涨占满所有 worker 进程,其他站点就会跟着 502。解决办法是给重要站点单独建一个 FPM 池:在 /etc/php/8.2/fpm/pool.d/ 下新建一个配置文件(比如 blog.conf),里面设置独立的 listen 端口或 socket、独立的用户和进程数上限:

[blog]
user = www-blog
group = www-blog
listen = /run/php/php8.2-fpm-blog.sock
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 10
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 5

然后在 Nginx 里把该站点的 fastcgi_pass 指向新的 socket,两个站点的 PHP 处理就完全隔离了。代价是多占一些内存,但对有重要业务的站长来说非常值得。内存紧张的服务器可以适当调小 max_children,按每个 PHP-FPM 进程平均占用 30M 内存估算,1G 内存的机器留出系统占用后,max_children 设 20 左右比较稳妥。

九、多站点常见坑与排错

最后总结新手最容易踩的五个坑。第一个坑是 server_name 写错导致访问一个域名却打开了另一个站点的内容,检查时用 curl -H "Host: example.com" http://IP/ 直接指定 Host 测试;第二个坑是忘记创建软链接,配置写了但没启用,nginx -T 可以列出当前实际生效的全部 server 块,一眼看出哪个没加载;第三个坑是 PHP-FPM 的 socket 权限问题,跨站点时报 502 或者 File not found,检查 fastcgi_param SCRIPT_FILENAME 是否使用了 $document_root;第四个坑是根目录权限不对导致 403,站点目录属主和 PHP-FPM 运行用户要一致;第五个坑是站点上线后改配置习惯用 restart 而不是 reload,restart 会瞬间中断所有正在处理的请求,高峰期操作可能造成用户访问报错,而 reload 是平滑重载,不影响现有连接,养成"改配置先 nginx -t 再 reload"的习惯,多站点管理就基本不会出大问题了。随着站点数量增加,这套规范化的管理方式会越来越体现出价值,也为你以后用 Ansible 之类的工具批量管理服务器打下了好基础。

Last modification:August 13th, 2026 at 09:13 am

Leave a Comment