Nginx 服务器块:在单台 VPS 上托管多个域名
通过完整配置示例,学习如何使用 Nginx 服务器块在一台 VPS 上运行多个网站,涵盖权限设置、按站点日志记录和默认服务器安全兜底。
一台 VPS 可以托管几十个网站。Nginx 使用**服务器块(server block)**根据请求中的域名决定提供哪个站点的内容。本教程涵盖完整配置流程:从零开始配置两个域名、安全默认值、按站点日志记录,以及每个步骤的验证。
本教程适用于 Debian 12 和 Ubuntu 24.04。两个系统使用相同的 sites-available/sites-enabled 模式,命令在两个系统上完全相同。
Nginx 服务器块是什么?
服务器块是 Nginx 配置中的一个配置块,定义如何处理针对特定域名的请求。它相当于 Apache 的虚拟主机(virtual host)。每个服务器块使用 listen 指令绑定端口,使用 server_name 指令匹配请求 Host 头中的域名。你可以配置任意数量的服务器块,它们共享 80 或 443 端口。
前置条件
开始之前,你需要:
- 已安装并运行 Nginx
- 两个域名,DNS A 记录指向你的服务器 IP
- 防火墙已放行 80 和 443 端口
- 以非 root 用户通过
sudo进行 SSH 访问
验证 Nginx 是否正在运行:
sudo systemctl status nginx
输出中应显示 active (running)。如果没有,启动并启用它:
sudo systemctl enable --now nginx
enable 参数使 Nginx 在重启后自动启动,--now 参数立即启动它。
验证你的 DNS 记录是否解析到你的服务器。在本教程中将域名替换为你自己的:
dig +short site-one.com
dig +short site-two.com
两者都应返回你服务器的公网 IP 地址。
如何为新域名创建服务器块?
整个流程分三部分:创建网站根目录、设置权限,以及编写服务器块配置文件。我们将配置两个域名:site-one.com 和 site-two.com。
应使用什么目录结构?
在 /var/www/ 下为每个站点创建独立的网站根目录:
sudo mkdir -p /var/www/site-one.com/html
sudo mkdir -p /var/www/site-two.com/html
为每个站点创建测试页面,以便验证哪个域名提供哪个内容:
echo '<h1>Site One</h1>' | sudo tee /var/www/site-one.com/html/index.html
echo '<h1>Site Two</h1>' | sudo tee /var/www/site-two.com/html/index.html
网站根目录需要什么权限?
Nginx 以 www-data 用户运行其 worker 进程。网站根目录需要该用户可读。
sudo chown -R www-data:www-data /var/www/site-one.com
sudo chown -R www-data:www-data /var/www/site-two.com
将权限设置为 755(所有者可读写执行,组和其他用户可读和遍历):
sudo chmod -R 755 /var/www/site-one.com
sudo chmod -R 755 /var/www/site-two.com
验证所有权和权限:
ls -la /var/www/
预期输出:
drwxr-xr-x 2 www-data www-data 4096 Mar 19 10:00 site-one.com
drwxr-xr-x 2 www-data www-data 4096 Mar 19 10:00 site-two.com
为什么用 www-data 而不是你自己的用户?在生产环境中,Web 服务器用户应该拥有静态文件。如果你的应用需要写文件(上传、缓存),Web 服务器用户需要写权限。使用你的个人用户会在 Web 漏洞和你的 SSH 账户之间打开一条通道。
创建服务器块配置文件
Debian 和 Ubuntu 上的 Nginx 使用两目录模式:配置文件存放在 /etc/nginx/sites-available/,通过创建符号链接到 /etc/nginx/sites-enabled/ 来激活。这样可以在不删除配置的情况下禁用站点。
为第一个域名创建服务器块:
sudo nano /etc/nginx/sites-available/site-one.com
server {
listen 80;
listen [::]:80;
server_name site-one.com www.site-one.com;
root /var/www/site-one.com/html;
index index.html;
access_log /var/log/nginx/site-one.com.access.log;
error_log /var/log/nginx/site-one.com.error.log;
location / {
try_files $uri $uri/ =404;
}
}
各指令的作用:
listen 80和listen [::]:80分别绑定 IPv4 和 IPv6 的 80 端口。server_name列出该块处理的域名,同时包含裸域名和www变体。root指向该站点的网站根目录。access_log和error_log写入按站点区分的日志文件。没有这些,所有站点共用/var/log/nginx/access.log,调试会很痛苦。try_files先尝试提供请求的文件,再尝试作为目录,最后返回 404。
创建第二个服务器块:
sudo nano /etc/nginx/sites-available/site-two.com
server {
listen 80;
listen [::]:80;
server_name site-two.com www.site-two.com;
root /var/www/site-two.com/html;
index index.html;
access_log /var/log/nginx/site-two.com.access.log;
error_log /var/log/nginx/site-two.com.error.log;
location / {
try_files $uri $uri/ =404;
}
}
如何启用和禁用服务器块?
sites-available/ 中的服务器块在链接到 sites-enabled/ 之前是不活跃的。
启用站点
为两个域名创建符号链接:
sudo ln -s /etc/nginx/sites-available/site-one.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/site-two.com /etc/nginx/sites-enabled/
移除 Nginx 自带的默认服务器块。它会与你的新块冲突,并提供 "Welcome to Nginx" 页面:
sudo rm /etc/nginx/sites-enabled/default
这只是移除符号链接,原始文件仍保留在 sites-available/ 中,以备后用。
应用前先测试配置
在重新加载之前,始终测试 Nginx 配置。一个文件中的语法错误会导致所有站点宕机:
sudo nginx -t
预期输出:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
如果需要查看完整的合并配置(所有 include 展开后的输出),使用:
sudo nginx -T
这是调试服务器块问题的最佳工具。它精确显示 Nginx 将加载的内容,包括服务器块的顺序。
重载而非重启
应用新配置:
sudo systemctl reload nginx
为什么用 reload 而不是 restart?重载会向 Nginx 发送信号,使其重新读取配置文件,现有连接正常处理完毕。重启则会杀掉进程并启动新进程,会断开所有活跃连接。生产环境中,始终使用重载。
验证重载是否成功:
sudo systemctl status nginx
注意观察:查看 active (running) 以及 "Main PID" 行的时间戳,时间戳不应改变(确认是重载而非重启)。
禁用站点
在不删除配置的情况下使站点下线:
sudo rm /etc/nginx/sites-enabled/site-two.com
sudo nginx -t && sudo systemctl reload nginx
配置文件仍保留在 sites-available/,随时可以重新创建符号链接来启用。
如何验证服务器块是否正常工作?
不要打开浏览器。在无头 VPS 上,使用带 Host 头的 curl 来模拟来自各域名的请求:
curl -H "Host: site-one.com" http://localhost
预期输出:
<h1>Site One</h1>
curl -H "Host: site-two.com" http://localhost
预期输出:
<h1>Site Two</h1>
这在 DNS 传播之前也有效,因为你通过 Host 头直接告诉 Nginx 你想要哪个域名。
DNS 传播完成后,从你的本地机器(而非服务器)测试:
curl http://site-one.com
curl http://site-two.com
每个请求都应返回正确的测试页面。
Nginx 如何决定哪个服务器块处理请求?
Nginx 按特定顺序将传入请求与服务器块匹配。请求到达时,Nginx 先按 listen 指令(IP 和端口)过滤服务器块,再将 Host 头与 server_name 值匹配。
server_name 的匹配顺序是什么?
匹配顺序是固定的,不能通过配置文件顺序改变:
| 优先级 | 匹配类型 | 示例 | 使用场景 |
|---|---|---|---|
| 1(最高) | 精确名称 | site-one.com |
始终优先尝试,最快(哈希查找)。 |
| 2 | 最长通配符前缀 | *.site-one.com |
匹配 www.site-one.com、api.site-one.com |
| 3 | 最长通配符后缀 | mail.* |
匹配 mail.site-one.com、mail.site-two.com |
| 4 | 首个匹配的正则 | ~^www\d+\.example\.com$ |
按配置文件顺序求值,第一个匹配获胜。 |
| 5(最低) | default_server |
无 | 没有其他匹配时的兜底。 |
关键点:
- 精确名称始终最先检查,与服务器块在配置中出现的位置无关。
- 通配符名称只能在点边界的开头或结尾使用
*,w*.example.com是无效的。 - 正则模式以
~开头,按配置文件中出现的顺序测试,第一个匹配获胜。应谨慎使用,因为它们是最慢的匹配类型。 - 如果没有匹配项且未设置
default_server,Nginx 将使用按配置文件顺序加载的第一个服务器块作为默认值,这是一个常见的混淆来源。
default_server 指令的作用是什么?
listen 指令上的 default_server 参数告诉 Nginx,当没有 server_name 匹配时,哪个服务器块处理请求。没有它,Nginx 会静默地选择该端口加载的第一个服务器块。这意味着配置错误的域名或扫描你 IP 的机器人会看到你的某个真实站点。
创建一个丢弃未匹配请求的兜底服务器块:
sudo nano /etc/nginx/sites-available/00-catch-all
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
return 444;
}
作用说明:
default_server将此块标记为 80 端口的兜底。server_name _是"无有效名称"的惯用写法,下划线对 Nginx 没有特殊含义,它只是永远不会匹配真实主机名。return 444是 Nginx 的非标准状态码,关闭连接而不发送任何响应。扫描你 IP 的机器人什么也得不到,没有头部、没有正文、没有关于你运行什么软件的信息。
启用并应用:
sudo ln -s /etc/nginx/sites-available/00-catch-all /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
文件名以 00- 开头,使其在目录列表中排在最前面。这对 Nginx 行为没有影响(由 default_server 指令控制选择,而非文件顺序),但便于人工扫描配置。
通过请求一个不匹配任何服务器块的域名来测试兜底:
curl -v -H "Host: not-my-domain.com" http://localhost
你应该看到 Empty reply from server,因为 Nginx 关闭了连接而没有响应。
如何设置按站点日志记录?
本教程中每个服务器块已包含按站点日志指令。按站点日志让你可以调试单个站点,而无需从所有其他域名的流量中筛选。日志路径在每个服务器块中设置:
access_log /var/log/nginx/site-one.com.access.log;
error_log /var/log/nginx/site-one.com.error.log;
实时追踪日志:
sudo tail -f /var/log/nginx/site-one.com.access.log
或使用 journalctl 查看 Nginx 主进程日志(启动错误、重载失败):
journalctl -u nginx -f
Nginx 通过 /etc/logrotate.d/nginx 自动处理日志轮转,默认策略每天轮转并保留 14 天,无需额外设置。
常见服务器块问题及解决方法
| 症状 | 原因 | 解决方法 |
|---|---|---|
| 所有域名都显示默认 "Welcome to Nginx" 页面 | default 符号链接仍在 sites-enabled/ 中 |
sudo rm /etc/nginx/sites-enabled/default && sudo systemctl reload nginx |
| 域名提供了错误的站点内容 | 多个块中有重复的 server_name,或未找到精确匹配 |
运行 sudo nginx -T | grep server_name 查找重复项 |
could not build server_names_hash 错误 |
域名对于默认哈希桶来说太长 | 在 /etc/nginx/nginx.conf 的 http {} 块内添加 server_names_hash_bucket_size 128; |
| 编辑配置后更改未生效 | 忘记重载 | sudo nginx -t && sudo systemctl reload nginx |
bind() to 0.0.0.0:80 failed |
另一个进程(Apache、旧版 Nginx)占用了 80 端口 | sudo ss -tlnp | grep :80 找到该进程,然后停止它 |
| 已创建符号链接但站点仍无法加载 | 符号链接指向错误路径(相对路径而非绝对路径) | 删除并重新创建:sudo ln -sf /etc/nginx/sites-available/site-one.com /etc/nginx/sites-enabled/ |
使用 nginx -T 调试
当站点行为不符合预期时,转储完整的合并配置:
sudo nginx -T 2>&1 | grep -A 5 "server_name"
这会显示每个服务器块及其 server_name 值,让你精确看到 Nginx 加载了什么以及加载顺序。它会解析所有 include 指令,所以你看到的是真实配置,而不仅仅是磁盘上的文件。
下一步
你的服务器块现在可以通过 HTTP 提供多个域名的服务。下一步是添加 TLS 证书,使这些站点通过 HTTPS 提供服务。
如需了解在 VPS 上管理 Nginx 的更全面视角,请参阅父级指南。
Copyright 2026 Virtua.Cloud. All rights reserved.