理解 Nginx 服务器块:常见配置问题
了解 Nginx 服务器块的工作原理——如何将请求匹配到正确的域名、块内包含什么内容,以及如何清晰地组织多站点配置。
理解 Nginx 服务器块:常见配置问题
理解 Nginx 服务器块是让 Nginx 配置不再神秘的最快方法之一。服务器块决定哪个站点处理请求、匹配哪些域名、提供哪些文件以及代理流量去向何处。
如果你托管多个域名、将 HTTP 重定向到 HTTPS,或在 Nginx 后面运行应用程序,服务器块就是大多数路由的起点。
什么是 Nginx 服务器块?
Nginx 服务器块是一段配置,定义了 Nginx 如何响应特定的地址、端口和主机名组合。
一个基本的静态站点服务器块如下所示:
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example.com/public;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
这告诉 Nginx 监听 80 端口,匹配 example.com 和 www.example.com 的请求,从 public 目录提供文件,并在找不到文件时返回 404。
“服务器块”这个名称在 Nginx 中很常见。Apache 用户可能知道类似的概念称为虚拟主机。目的相同:允许一个 Web 服务器处理多个站点或应用程序。
在 Debian 和 Ubuntu 系统上,服务器块通常存储在 /etc/nginx/sites-available/ 中,并通过符号链接启用至 /etc/nginx/sites-enabled/。某些发行版使用 /etc/nginx/conf.d/ 代替。具体布局不如知道哪些文件被 nginx.conf 包含重要。
Nginx 如何选择正确的服务器块?
Nginx 的选择分阶段进行。首先,它考虑 listen 指令中的 IP 地址和端口。然后,它将请求的主机名与 server_name 进行比较。
如果没有匹配的主机名,Nginx 会使用该地址和端口的默认服务器。你可以显式标记一个:
server {
listen 80 default_server;
server_name _;
return 444;
}
一些团队使用这样的默认块来丢弃不匹配的请求。其他人则返回一个简单的 404。最佳选择取决于你的日志记录和安全偏好。
如果你还在学习,可见的 404 默认值比 return 444 更容易调试,因为 444 是 Nginx 特定的连接关闭,从客户端角度看可能像网络问题。在生产环境中,一些团队更喜欢对随机不匹配的主机进行静默关闭。只要团队理解,任何一种选择都可以。
精确名称优先于通配符名称。例如,api.example.com 比 *.example.com 更具体。正则表达式服务器名称是可能的,但应很少使用,因为它们更难阅读和故障排除。
一个常见错误是向 DNS 添加新域名但忘记将其添加到 server_name。请求到达服务器,但 Nginx 将其发送到默认块。从用户的角度看,会显示错误的站点或请求失败。
另一个常见错误是创建两个都声明相同 listen 和 server_name 的块。Nginx 可能会警告冲突的服务器名称并忽略其中一个。添加站点后始终测试配置。
当错误的站点响应时,使用以下命令:
sudo nginx -T | grep -n "server_name"
curl -I -H 'Host: example.com' http://127.0.0.1/
nginx -T 打印完整的加载配置,包括包含的文件。这通常比打开 sites-enabled 下的每个文件更快。
服务器块内包含什么?
服务器块通常包含域名匹配、TLS、文档根目录、日志记录、重定向以及一个或多个 location 块的指令。
对于反向代理,块可能如下所示:
server {
listen 443 ssl;
server_name app.example.com;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://127.0.0.1:3000;
}
}
这里,Nginx 终止 HTTPS 并将流量转发到本地端口 3000 上的应用程序。这对于 Node.js、Python、Ruby、Go 和 Java 应用程序很常见。
如果上游应用程序需要知道原始方案或客户端 IP,这些 proxy_set_header 行很重要。没有它们,应用程序可能认为每个请求都来自 127.0.0.1 且通过普通 HTTP。这可能会破坏重定向、审计日志、速率限制和生成的回调 URL。
托管多个站点时使用清晰的日志路径:
access_log /var/log/nginx/app.example.com.access.log;
error_log /var/log/nginx/app.example.com.error.log;
单独的日志使故障排除更加容易。当每个站点写入一个访问日志时,需要更长时间才能发现哪个域名失败。
对于繁忙的主机,这也使保留更容易。你可能希望在事件期间为 API 保留详细日志,而为静态站点保留较轻的日志。保持文件分离可以让你在不一次更改每个服务器块的情况下拥有该选项。
服务器块与位置块有何不同?
服务器块选择站点。位置块选择在该站点内对路径做什么。
例如:
server {
server_name example.com;
location /assets/ {
expires 30d;
}
location /api/ {
proxy_pass http://api_backend;
}
location / {
try_files $uri $uri/ /index.html;
}
}
对 /assets/logo.png 的请求获得静态文件缓存。对 /api/users 的请求转到上游后端。其他请求回退到前端应用程序。
这种分离很重要。如果错误的域名响应,请查看服务器块匹配。如果正确的域名响应但路径行为错误,请查看位置匹配。
这种区分可以节省时间。对 api.example.com/users 的请求可能因为 api.example.com 匹配了错误的服务器块而失败,或者因为 /users 在正确的块内匹配了错误的位置。这些是不同的错误。
有关路径路由的更多详细信息,请参阅 Nginx 位置块解释。
如何组织多个站点?
尽可能为每个站点或应用程序使用一个文件。给每个文件一个与域名或用途匹配的名称:
/etc/nginx/sites-available/example.com
/etc/nginx/sites-available/api.example.com
/etc/nginx/sites-available/admin.example.com
这使审查更容易,并减少更改错误服务的可能性。当你需要快速禁用站点时,这也很有帮助。
保持共享片段小而明确。TLS 设置、代理标头和安全标头是包含的好候选。避免将主要路由行为隐藏在通用包含文件中,因为这会使调试更加困难。
一个实际的设置可能包括:
include snippets/proxy-headers.conf;
include snippets/security-headers.conf;
使用包含来减少重复,而不是使每个站点行为相同。内部管理面板、公共 API 和静态营销站点通常需要不同的限制和标头。
在删除或重命名服务器块文件之前,请检查它如何被包含。在 Debian 风格的布局中,如果另一个副本仍然存在于 conf.d 中,从 sites-available 中删除文件不会产生任何效果。另一方面,删除 sites-enabled 中的符号链接会禁用站点而不删除源文件。
ls -l /etc/nginx/sites-enabled/
sudo nginx -T | grep -n "include"
这个快速检查可以防止令人沮丧的情况,即你编辑了看起来正确的文件,但 Nginx 一直使用另一个文件。
何时寻求服务器块问题的帮助
当服务器块更改影响生产域名、HTTPS 证书、面向客户的重定向或同一主机上的多个应用程序时,请向 DevOps 工程师或 Nginx 专家寻求帮助。小错误可能会将用户发送到错误的应用程序或破坏证书续订。
如果你认为配置正确但 Nginx 仍然提供错误的站点,你也应该寻求帮助。问题可能涉及 DNS、IPv6、负载均衡器、CDN 或你没有注意到的包含文件。
服务器块是 Nginx 用于路由域名级流量的地图。保持它们具体、每次更改后测试、使用单独的日志,并在你的思维模型中分离域名匹配和路径路由。一旦理解这一点,大多数 Nginx 配置问题就变得容易回答得多。