Nginx中proxy_pass支持变量实现动态代理,但变量仅可用于URI部分且必须以/结尾;推荐用map白名单映射保障安全;rewrite可辅助提取变量,需避免用户输入直拼和嵌套引用。

Nginx 中可以通过 proxy_pass 指令结合变量(如 $host、$arg_backend、自定义 map 或 set 变量)实现动态后端代理,但需注意几个关键限制和正确用法。
变量必须出现在 proxy_pass 的 URI 部分(非协议+主机部分)
Nginx 官方明确要求:当 proxy_pass 值中包含变量时,不能只写域名或 IP,必须带完整 URI(即以 / 开头的路径)。否则会报错 "proxy_pass" cannot have a URL with variables and no URI。
✅ 正确写法(带 URI 路径):
location /api/ {
set $backend "http://192.168.1.10:8000";
proxy_pass $backend/;
}
⚠️ 错误写法(无 URI):
location /api/ {
set $backend "http://192.168.1.10:8000"; # ❌ 缺少末尾 '/'
proxy_pass $backend; # 报错!
}
用 map 模块实现安全可控的后端路由映射
直接拼接用户输入(如 $arg_backend)有严重安全风险(如 SSRF)。推荐用 map 预定义白名单映射关系,既动态又安全。
示例:根据请求参数选择后端服务
# 在 http 块中定义映射
map $arg_env $upstream_addr {
default "127.0.0.1:3000";
prod "10.0.1.5:8080";
staging "10.0.1.6:8080";
dev "127.0.0.1:3001";
}
<p>server {
location /app/ {
proxy_pass <a href="https://www.php.cn/link/53b765f9a43e37ed2076c45902067b1f">https://www.php.cn/link/53b765f9a43e37ed2076c45902067b1f</a>;
proxy_set_header Host $host;
}
}</p>-
map是惰性求值,只在实际用到时解析,性能开销小 - 未匹配的
$arg_env会走default分支,避免空值导致代理失败 - 所有后端地址都写死在配置中,不暴露给客户端,杜绝注入风险
用 rewrite + proxy_pass 实现路径级动态转发
若需根据路径前缀或请求头决定后端,可先用 rewrite 提取变量,再交给 proxy_pass。
示例:按子域名路由到不同集群
map $host $cluster {
~^([a-z0-9]+)\.example\.com$ $1;
}
<p>server {
server_name ~^(?<sub>.+).example.com$;</p><pre class='brush:php;toolbar:false;'>location / {
# 根据 sub 变量查 map 得到 cluster 名,再映射为真实地址
set $backend "https://$cluster-api.internal/";
proxy_pass $backend;
proxy_set_header Host $host;
}}
注意:set 和 map 可嵌套使用,但变量展开只支持一层间接引用(Nginx 不支持类似 ${${var}} 的嵌套)。
避免常见陷阱
-
不要在
proxy_pass中直接拼接用户参数,如proxy_pass http://$arg_backend/;—— 极易被利用发起内网探测 -
URI 末尾斜杠影响路径重写逻辑:如果
proxy_pass以/结尾,Nginx 会剥离location匹配的前缀;不加则原样传递 -
变量必须在
location块内可访问:set必须在location内或server级定义,且不能跨上下文引用未声明变量 -
HTTPS 后端需启用
proxy_ssl_*系列指令,否则连接会被拒绝


















