Nginx虚拟主机:在一台服务器上托管多个网站
解锁Nginx虚拟主机(服务器块)的强大功能,高效地在单台服务器上托管多个网站或子域名。本指南提供全面的分步教程,涵盖目录设置、配置文件创建、启用服务器块和Nginx测试。学习子域名、默认服务器块、HTTPS集成和专用日志记录的最佳实践。通过实用示例和基本故障排除技巧,掌握多站点Nginx托管,优化资源使用并简化Web服务器管理。
Nginx虚拟主机:在一台服务器上托管多个网站
在一台服务器上运行几个小站点是Nginx的常规任务。你可能有一个公司网站、一个文档站点、一个临时应用和一个客户门户,全部部署在同一台机器上。Nginx通过server块将它们分开。Apache用户通常称同样的概念为虚拟主机,因此你会在教程和托管面板中看到这两个术语。
关键部分很简单:Nginx查看端口、IP地址和Host头,然后选择匹配的server块。如果加载了错误的站点,问题通常并不神秘。通常是缺少DNS记录、server_name中的拼写错误、默认服务器捕获了请求,或者两个文件声称同名。
理解Nginx服务器块(虚拟主机)
核心上,Nginx的服务器块是在Nginx配置文件(nginx.conf或包含的文件)中定义的配置指令。每个server块定义特定虚拟主机的配置,指示Nginx如何响应特定域名或域名集合的请求。Nginx使用listen指令指定要监听的IP地址和端口,使用server_name指令标识此服务器块应响应的域名或主机名。
当请求到达时,Nginx检查HTTP请求的Host头,并将其与配置的服务器块的server_name指令进行比较。然后,它提供匹配服务器块中定义的内容。如果没有server_name匹配,Nginx通常会回退到默认服务器块(第一个server块或明确标记为default_server的块)。
前提条件
开始之前,请确保满足以下条件:
- 已安装Nginx:Nginx应已安装并在服务器上运行。如果没有,通常可以通过系统的包管理器安装(例如,Ubuntu/Debian上使用
sudo apt update && sudo apt install nginx,CentOS/RHEL上使用sudo yum install nginx)。 - 域名:你需要至少两个域名(例如,
example1.com和example2.com)或子域名(例如,blog.example.com和app.example.com)来托管。这些域名的DNS A/AAAA记录必须指向你服务器的公共IP地址。 - 基本目录结构:规划网站文件存放的位置。常见做法是
/var/www/yourdomain.com/html。 - Sudo权限:你需要
sudo访问权限来修改Nginx配置文件。
分步设置指南
让我们设置两个虚拟主机:example1.com和example2.com。
步骤1:为网站创建目录结构
首先,为每个网站创建根目录。这些目录将存储它们的HTML、CSS、JavaScript和其他静态文件。常见位置是/var/www/。
sudo mkdir -p /var/www/example1.com/html
sudo mkdir -p /var/www/example2.com/html
# 将所有权设置为你的用户(将$USER替换为你的用户名)以允许编辑
sudo chown -R $USER:$USER /var/www/example1.com/html
sudo chown -R $USER:$USER /var/www/example2.com/html
# 为Web服务器设置读取权限
sudo chmod -R 755 /var/www
接下来,在每个目录中创建一个简单的index.html文件以测试设置:
对于/var/www/example1.com/html/index.html:
<!-- /var/www/example1.com/html/index.html -->
<!DOCTYPE html>
<html>
<head>
<title>欢迎来到Example1.com!</title>
</head>
<body>
<h1>成功!这是Example1.com。</h1>
<p>此虚拟主机工作正常。</p>
</body>
</html>
对于/var/www/example2.com/html/index.html:
<!-- /var/www/example2.com/html/index.html -->
<!DOCTYPE html>
<html>
<head>
<title>欢迎来到Example2.com!</title>
</head>
<body>
<h1>成功!这是Example2.com。</h1>
<p>此虚拟主机也在工作!</p>
</body>
</html>
步骤2:创建Nginx服务器块配置文件
Nginx通常从/etc/nginx/sites-enabled/目录中的文件加载服务器块配置。这些文件通常是存储在/etc/nginx/sites-available/中的配置的符号链接。这种分离允许你存储尚未激活的配置,或轻松启用/禁用站点。
为example1.com创建一个新的配置文件:
sudo nano /etc/nginx/sites-available/example1.com.conf
添加以下内容:
# /etc/nginx/sites-available/example1.com.conf
server {
listen 80;
listen [::]:80;
root /var/www/example1.com/html;
index index.html index.htm index.nginx-debian.html;
server_name example1.com www.example1.com;
location / {
try_files $uri $uri/ =404;
}
access_log /var/log/nginx/example1.com_access.log;
error_log /var/log/nginx/example1.com_error.log;
}
指令说明:
listen 80;:Nginx监听80端口(标准HTTP)。listen [::]:80;用于IPv6。root /var/www/example1.com/html;:指定此服务器块的文档根目录。Nginx将在此目录中查找文件。index index.html ...;:定义请求目录时Nginx应提供的默认文件(例如,当有人访问example1.com/时)。server_name example1.com www.example1.com;:这很关键。它告诉Nginx使用此服务器块的配置响应example1.com或www.example1.com的请求。location / { ... }:定义如何处理特定URI请求的块。try_files尝试直接提供文件($uri),然后是目录($uri/),最后返回404 Not Found错误。access_log和error_log:为此特定站点指定单独的日志文件,这是便于调试和分析的良好实践。
现在,为example2.com创建一个类似的配置文件:
sudo nano /etc/nginx/sites-available/example2.com.conf
添加以下内容:
# /etc/nginx/sites-available/example2.com.conf
server {
listen 80;
listen [::]:80;
root /var/www/example2.com/html;
index index.html index.htm index.nginx-debian.html;
server_name example2.com www.example2.com;
location / {
try_files $uri $uri/ =404;
}
access_log /var/log/nginx/example2.com_access.log;
error_log /var/log/nginx/example2.com_error.log;
}
步骤3:启用服务器块
要启用这些配置,从sites-available目录创建符号链接到sites-enabled目录。这告诉Nginx在启动时包含这些文件。
sudo ln -s /etc/nginx/sites-available/example1.com.conf /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/example2.com.conf /etc/nginx/sites-enabled/
步骤4:测试Nginx配置
在重新加载之前,测试Nginx配置是否存在语法错误至关重要。这可以防止由于拼写错误导致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
如果看到任何错误,请在相应的配置文件中修复它们,并重新运行sudo nginx -t直到通过。
步骤5:重启Nginx
通过重启或重新加载Nginx来应用新配置。reload通常更可取,因为它允许Nginx加载新配置而不中断活动连接。
sudo systemctl reload nginx
# 或者,如果reload不起作用或对于全新安装:
sudo systemctl restart nginx
步骤6:更新DNS记录
确保example1.com、www.example1.com、example2.com和www.example2.com的DNS A记录都指向你的Nginx服务器的IP地址。如果没有正确的DNS条目,你的浏览器将不知道在哪里找到你的网站。
一旦DNS传播完成(可能需要几分钟到几小时),你应该能够在Web浏览器中访问http://example1.com和http://example2.com,并看到相应的index.html页面。
高级场景和最佳实践
托管子域名
托管子域名(例如,blog.example.com、shop.example.com)的工作方式与托管独立域名完全相同。只需使用子域名作为server_name定义一个新的服务器块。
blog.example.com的示例:
# /etc/nginx/sites-available/blog.example.com.conf
server {
listen 80;
listen [::]:80;
root /var/www/blog.example.com/html;
index index.html;
server_name blog.example.com;
location / {
try_files $uri $uri/ =404;
}
}
记得创建目录(/var/www/blog.example.com/html)、创建index.html、创建符号链接并重新加载Nginx。
默认服务器块
拥有一个默认服务器块是一个好习惯,它可以捕获与服务器上任何其他server_name指令不匹配的域名请求。这可以防止未知请求被Nginx找到的“第一个”虚拟主机提供,或者允许你提供通用的“站点未找到”页面。
通常,nginx.conf或sites-enabled中的第一个server块隐式地是默认的。你可以使用default_server显式设置一个:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
# 下划线`_`是一个不存在的域名,永远不会匹配真实请求。
# 你也可以使用localhost。
root /var/www/default_site/html;
index index.html;
location / {
return 444; # 对未知主机返回Nginx特定的444错误(无响应)
# 或者,提供通用着陆页:
# try_files $uri $uri/ =404;
}
}
警告:如果你定义了一个default_server块,确保在给定的listen端口上只有一个server块具有default_server标志,否则Nginx会记录警告。
使用HTTPS(SSL/TLS)保护虚拟主机
对于生产网站,启用HTTPS是必不可少的。这涉及获取SSL/TLS证书(例如,通过使用Certbot的Let's Encrypt)并配置Nginx在端口443上监听并附带证书。
一个典型的HTTPS服务器块如下所示(获取证书后):
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example1.com www.example1.com;
root /var/www/example1.com/html;
index index.html;
ssl_certificate /etc/letsencrypt/live/example1.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example1.com/privkey.pem;
# 包含其他SSL配置(密码、协议等)
include /etc/nginx/snippets/ssl-params.conf;
location / {
try_files $uri $uri/ =404;
}
}
# 可选:此域名的HTTP到HTTPS重定向
server {
listen 80;
listen [::]:80;
server_name example1.com www.example1.com;
return 301 https://$host$request_uri;
}
通常有一个单独的HTTP服务器块,其唯一目的是将所有流量重定向到其HTTPS对应项。
如果你使用Certbot,它可能会为你创建或编辑这些块。这很方便,但你仍然应该阅读生成的文件。自动化证书工具有时会在你本不会选择的位置添加重定向逻辑,重复的重定向会使故障排除更加困难。
每个站点的日志记录
如示例所示,为每个虚拟主机分配单独的access_log和error_log文件是一种最佳实践。这使得调试问题和分析单个网站的流量变得更容易,而无需筛选合并的日志。
配置文件结构
对于较大的部署,考虑按如下方式组织你的Nginx配置文件:
nginx.conf:主配置,包含conf.d/*.conf和sites-enabled/*。conf.d/:通用的服务器范围设置(例如,Gzip、缓存)。snippets/:可重用的Nginx配置片段(例如,SSL参数、常见的location块)。sites-available/:每个网站的单独server块。sites-enabled/:指向sites-available/中活动配置的符号链接。
常见问题故障排除
- 403 Forbidden错误:这通常意味着Nginx没有读取网站文件或目录的权限。仔细检查文件和目录权限(例如,
sudo chmod -R 755 /var/www/yourdomain.com/html,并确保Nginx用户(通常是www-data或nginx)可以读取它们)。 - 404 Not Found错误:验证服务器块中的
root指令是否指向正确的目录,并且你的index.html文件存在于该位置。另外,确保try_files配置正确。 - 加载了错误的站点:这通常表示
server_name指令有问题。确保server_name与你尝试访问的域名完全匹配(包括www.或子域名)。同时,检查你的DNS记录。 - Nginx无法启动/重新加载:在尝试重新加载或重启Nginx之前,始终使用
sudo nginx -t测试配置。错误消息将指出语法错误发生的行和文件。 - DNS问题:如果你可以通过IP地址访问站点,但无法通过域名访问,那几乎肯定是DNS问题。使用
dig或nslookup验证域名的A记录是否指向正确的服务器IP。
在DNS就绪前进行测试
你不必等待公共DNS来测试Nginx端。你可以发送带有自定义Host头的请求:
curl -H "Host: example1.com" http://203.0.113.10/
curl -H "Host: example2.com" http://203.0.113.10/
将203.0.113.10替换为你的服务器IP。如果每个命令返回正确的测试页面,则服务器块匹配正常工作。如果两个命令返回相同的页面,检查两个文件是否已启用,server_name是否正确,以及默认块是否捕获了请求。
对于HTTPS,测试略有不同,因为TLS在处理HTTP Host头之前使用SNI:
curl --resolve example1.com:443:203.0.113.10 https://example1.com/
该命令告诉curl连接到你的服务器IP,同时仍然使用example1.com进行TLS和HTTP。这是在更改DNS之前测试新HTTPS虚拟主机的最快方法之一。
可维护的多站点模式
对于少数静态站点,上面的示例就足够了。一旦你托管多个应用程序,减少重复并仅集中真正共享的部分。例如,将常见的安全头、压缩和SSL参数放在片段中,但保持每个站点的root、server_name、upstream和日志在其自己的文件中可见。
避免不阅读每一行就将一个大型生产块从一个域复制到另一个域。这就是server_name错误、错误的证书路径和共享日志文件潜入的方式。一个实用的审查清单很短:
server_name是否包含用户将键入的所有主机名?root或proxy_pass是否指向此站点,而不是前一个?- 访问和错误日志是否足够分离以便单独调试此站点?
- 重新加载前
nginx -t是否通过? curl -H "Host: ..."或curl --resolve是否返回预期的站点?
最后说明
当每个站点都有清晰的服务器块、正确的server_name和可预测的默认回退时,Nginx虚拟主机是可靠的。保持文件简单。在重新加载之前测试每个更改。当站点重要时使用专用日志。一旦你能证明DNS、TLS/SNI或服务器块匹配是失败的部分,大多数多站点Nginx问题就变得容易解决了。