VPS 跑到第二三个站点是必然的——域名便宜了,谁家没有几个想法要试。一机多站本身不难,难的是“多而不乱”:三个站混在一起,日志搅一团、一个站的 PHP 疯了拖死全机、改 A 站配置误伤 B 站。这篇讲一套组织规范,把多站变成“逻辑上各住各家”。Nginx 的多站机制《Nginx 装好了只会看欢迎页:手把手部署你的第一个站点》讲过基础(server 块加 sites-enabled 软链),这里往前走三步:目录规范、独立 PHP-FPM 池、文件权限隔离。
第一步,目录按“一城一房”立规矩。每个站点一个顶层目录,内部结构固定:
/var/www/
├── example.com/
│ ├── public/ # 网站根目录(Nginx只指向这里)
│ ├── logs/ # 该站访问/错误日志
│ └── conf/ # 该站专属的额外配置
├── blog.example.com/
│ ├── public/
│ ├── logs/
│ └── conf/
└── another-site.com/
└── ...
logs 独立是很多教程不强调但极其重要的一点:三个站共用一个 access.log,排查问题时你要在几万行里猜哪行属于哪个站;分开后每站自己的日志,还方便按站配 logrotate(《logrotate日志轮转:磁盘满的病根在日常,一次配置管三年》)。Nginx 的 server 块规范写法:
# /etc/nginx/sites-available/blog.example.com
server {
listen 80;
server_name blog.example.com;
root /var/www/blog.example.com/public;
index index.php index.html;
access_log /var/www/blog.example.com/logs/access.log;
error_log /var/www/blog.example.com/logs/error.log;
location ~ \.php$ {
include snippets/fastcgi-php.conf;
# 关键:指向该站专属的FPM池(下面第二步建)
fastcgi_pass unix:/run/php/php-fpm-blog.sock;
}
}
# 启用:软链到sites-enabled后nginx -t && nginx -s reload
ln -s /etc/nginx/sites-available/blog.example.com /etc/nginx/sites-enabled/
第二步,独立 PHP-FPM 池,这是多站隔离的核心。默认所有站点共用 www 池:A 站被人打了,请求把池里进程占满,B 站 C 站跟着 502——《PHP-FPM 进程耗尽:pm.max_children 用内存反推怎么算》的 pm.max_children 耗尽问题会按站传染。给每个站一个独立池,资源边界就画出来了:
# /etc/php/8.3/fpm/pool.d/blog.conf
[blog]
user = bloguser
group = bloguser
listen = /run/php/php-fpm-blog.sock
listen.owner = www-data
pm = dynamic
pm.max_children = 10 ; 这个站的进程上限
pm.start_servers = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 4
php_admin_value[memory_limit] = 128M ; 单请求内存上限也按站管
sudo systemctl reload php8.3-fpm
每个池配套一个系统用户(useradd -r -s /sbin/nologin bloguser),站点文件属主归它。这一步直接带来第三层的文件权限隔离:
# A站的文件属主是bloguser,B站用户(wpuser)读不到核心文件
chown -R bloguser:bloguser /var/www/blog.example.com
chmod -R 750 /var/www/blog.example.com
# 效果验证:以wpuser身份尝试读blog站文件
sudo -u wpuser cat /var/www/blog.example.com/wp-config.php
# 期望:Permission denied
# 就算B站被传了WebShell(《网站被挂马被跳转应急排查:文件比对、计划任务与 .user.ini》),也能被这个边界拦在A站门外
这套隔离的价值用一个场景说透:三站一池的机器,任何一个站被拿下(挂马、刷请求),攻击者面对的是全机;一池一站加独立用户,攻击面被压到那个站自己的目录里。《网站被挂马被跳转应急排查:文件比对、计划任务与 .user.ini》讲挂马应急时最痛的就是“不知道马还能不能碰到别的站”,独立用户从权限层面回答了这个问题——碰不到。代价是每池几十 MB 常驻内存,按《宝塔还是 1Panel:2026 年面板选型的实测账本》的内存账本算好再开。
Nginx 层还有个多站特有的陷阱要交代:server_name 匹配不到时的落点。请求到达 Nginx 后按 Host 头找匹配的 server 块,找不到就落进 default_server(不显式指定时是该端口第一个加载的 server 块)。后果是有人把垃圾域名解析到你的 IP,请求全落到你某个站点上,给它的日志刷垃圾流量甚至蹭你的缓存。规范做法是显式声明一个兜底块:listen 80 default_server; return 444;——444 是 Nginx 的私有状态码,直接掐断连接不回包,垃圾解析请求全部闭嘴。每个站点的 server 块则写清楚自己的 server_name,谁也不串门。
日常维护的几条纪律:新站上线先复制模板 server 块和 pool 文件,改四处(域名、目录、sock 名、用户名)再 nginx -t 验证;所有站点共用的一份证书(通配符证书)可以,但配置文件别共用;每季度跑一遍 ps aux | grep php-fpm 看各池占用,某个池常年顶到 max_children,就按《PHP-FPM 进程耗尽:pm.max_children 用内存反推怎么算》调参数或升级机器。多站不可怕,没规范的多站才可怕。
相关阅读:第二个站点上线前,先看本篇把多站规矩立好。《从零建站第一步:技术栈选型总纲,别一上来就纠结框架》的运营组覆盖扩站、克隆、备案,总纲里按需取用。
申请创业报道,分享创业好点子。点击此处,共同探讨创业新机遇!
