Nginx动态代理需满足两个前提:proxy_pass必须使用变量,且变量值须为含协议和路径的完整URI;推荐用map预定义可信后端映射,安全可控;动态DNS解析需配合resolver与set同级配置;复杂路由可结合正则命名捕获实现。

Nginx 本身不支持在 proxy_pass 指令中直接拼接 URI 路径与变量(比如 http://$host:$port/api),因为其 URI 解析发生在配置加载阶段,而变量值只在请求处理时才可获取。但通过合理组合 Nginx 原生机制,完全可以实现安全、可控、可维护的动态代理目标地址生成。
必须满足的两个硬性前提
所有动态 proxy_pass 场景都依赖以下两点同时成立:
-
proxy_pass 的值必须是变量:不能写死域名或 IP,必须形如
proxy_pass $backend;或proxy_pass https://$host$request_uri; -
变量必须构成合法完整 URI:即含协议(
http://或https://)、主机(域名或 IP)、可选端口,且不能缺失路径部分(哪怕只是/)。例如http://api.example.com/✅,api.example.com❌,http://$host❌(缺路径)
推荐方案:用 map 预定义可信后端映射
这是最安全、最清晰、无需额外模块的方式。它把“逻辑判断”提前到配置解析期,避免运行时拼接带来的注入、空值、格式错误等风险。
示例:根据请求头 X-Env 切换测试/生产环境
map $http_x_env $backend {
default "http://default-api.internal/";
"staging" "http://staging-api.internal/";
"prod" "http://prod-api.internal/";
}
然后在 location 中直接引用:
location /api/ {
proxy_pass $backend;
proxy_set_header Host $host;
}
注意:$backend 的值已包含结尾斜杠 /,确保路径重写行为符合预期;所有值都经过人工审核,无外部输入污染。
进阶需求:动态 DNS 解析(每次请求前查新 IP)
若后端域名 IP 经常变动(如云服务弹性实例、K8s Service),需避免 Nginx 启动时缓存旧地址。唯一可靠方式是:变量 + resolver 同级配置。
正确写法(放在 location 内):
location /service/ {
resolver 114.114.114.114 8.8.8.8 valid=10s;
set $target "svc.example.com";
proxy_pass https://$target;
}
关键点:
-
resolver必须和set、proxy_pass在同一作用域(推荐 location 级) -
$target只存纯域名,不带协议、端口、路径 -
proxy_pass中显式写出协议(https://),端口由协议默认值或 DNS SRV 记录决定 -
valid=10s控制 DNS 缓存最大时长,超时后自动重查
复杂路由:结合正则提取 + 变量转发
适用于子域名或路径前缀映射到不同后端的场景,例如 a.example.com → http://a-svc:8000。
步骤分三步:
- 在
server_name中用命名捕获提取前缀:server_name ~^(?<service>[a-z]+)\.example\.com$;</service> - 在 location 中引用该变量:
location / { proxy_pass http://$service-svc:8000; }
⚠️ 注意:$service 是 server 级变量,在 location 中可用;但不可用于 upstream 名称拼接(upstream 名称必须静态定义);也不建议用于构造含端口的 URL(旧版 Nginx 可能报错),稳妥做法是统一用 upstream 块封装,再用 map 映射名称。
不复杂但容易忽略:动态代理的核心不是“能不能拼”,而是“怎么拼得稳”。用 map 控制源头,用 resolver 管理解析,用命名变量明确语义——这样既发挥 Nginx 原生能力,又守住运维底线。


















