Nginx中proxy_pass直接写域名(如http://adh-web:3000)会在启动时静态解析一次并缓存IP,后续请求始终发往该固定地址;若后端IP变更(如容器重启),因无动态刷新机制,将导致connect失败。唯一可靠解法是同时满足:location/server块内配置resolver指令(含valid=参数)且proxy_pass必须使用变量(如proxy_pass http://$upstream_web;)。

为什么 proxy_pass 直接写域名会固定解析一次
Nginx 启动时对 proxy_pass http://adh-web:3000 这类静态写法做 DNS 解析,把 adh-web 硬编码成当时查到的 IP(比如 10.89.1.17),之后不再刷新。容器重启后 IP 变成 10.89.1.20,Nginx 仍往旧地址发请求,connect() failed (113: No route to host) 就必然出现。
resolver + 变量是唯一可靠的动态方案
必须同时满足两个条件,Nginx 才会在每次请求时检查 DNS 缓存:
-
resolver指令要放在location或server块里,且带valid=参数(如valid=10s) -
proxy_pass的目标必须是变量,不能是字面量域名
正确写法示例:
location / {
resolver 10.89.1.1 valid=10s ipv6=off;
set $upstream_web "adh-web:3000";
proxy_pass http://$upstream_web;
}
注意:resolver 地址得是 Podman/Containerd 网络实际提供的 DNS(通常是网关 IP),不是 127.0.0.1 或 8.8.8.8;ipv6=off 能避免因 IPv6 解析失败拖慢请求。
常见踩坑点:resolver 放错位置或没配 valid
这些写法都无效:
- 把
resolver写在http块顶层 —— Nginx 会忽略,仍走静态解析 - 漏掉
valid=参数 —— 默认缓存永不过期,等于白配 -
proxy_pass写成http://adh-web:3000却以为加了resolver就能动态 —— 变量是强制要求,不是可选项 - 用
upstream块包裹域名 —— upstream 里的域名也是启动时解析,resolver对它完全不生效
HTTPS upstream 的额外要求:SNI 必须透传
如果后端是 HTTPS 域名(如 https://api.example.com),光靠 resolver 不够。Nginx 默认用 IP 建连,不发 SNI 扩展,后端 TLS 终止服务(如 Nginx、Traefik)无法匹配证书,直接断连,报 502。
必须显式开启:
-
proxy_ssl_server_name on;—— 让 Nginx 在 TLS 握手时发送 SNI -
proxy_ssl_name "api.example.com";—— 指定 SNI 字段值(必须和证书 Subject Alternative Name 一致) -
proxy_set_header Host "api.example.com";—— 避免后端基于 Host 判定路由失败
变量写法同样适用:
location /api/ {
resolver 10.89.1.1 valid=10s;
set $backend "api.example.com";
proxy_pass https://$backend;
proxy_ssl_server_name on;
proxy_ssl_name $backend;
proxy_set_header Host $backend;
}
真正容易被忽略的是:SNI 和 DNS 动态解析是两层独立机制,缺一不可。只配 resolver,HTTPS 后端照样 502;只开 proxy_ssl_server_name,但 IP 没更新,还是连不上。


















