nginx中需用location正则匹配+rewrite重写路径才能将动态变量传给后端,proxy_pass不支持直接插值;可配合map/set实现动态路由,再通过proxy_set_header透传原始路径或变量。

在 Nginx 中做 HTTP 代理时,想让后端服务收到的路径包含动态提取的变量(比如版本号、租户名、ID 等),不能直接在 proxy_pass 里写 $1 或 $2 —— 它不支持这种变量插值。必须先用正则匹配并重写 URI,再交给 proxy_pass 转发。
用 location 正则捕获 + rewrite 重写路径
这是最常用也最稳妥的方式:在 location 块中用带括号的正则匹配请求路径,再用 rewrite 提取并构造新路径,最后由 proxy_pass 转发重写后的 URI。
-
示例:把
/api/v2/users/123映射为/users/123并转发到后端
location ~ ^/api/v(\d+)/(.*)$ {
rewrite ^/api/v\d+/(.*)$ /$1 break;
proxy_pass http://backend;
} -
$1是rewrite中正则捕获的路径部分(如users/123),break表示重写后不再匹配其他 location - 注意:
proxy_pass后不跟路径(如/),否则会丢掉重写结果;若带路径,则以它为准,rewrite的路径会被忽略
用命名捕获组 + map 或 set 构建动态目标
当需要根据路径变量决定转发到不同后端时,可结合 map 或 set 提前提取并赋值,再用于 proxy_pass。
-
示例:按子域名或路径前缀路由到不同服务
server_name ~^(?[a-z]+)\.example\.com$;
location / {
set $backend http://$service:8000;
proxy_pass $backend;
} - 或更安全的做法是用
map预定义白名单映射:
map $service $upstream {
default "http://default:8000";
user "http://user-svc:8000";
order "http://order-svc:8000";
}
然后proxy_pass $upstream; - 关键点:
proxy_pass的变量必须是完整 URI(含协议、主机、路径),且不能为空
配合 proxy_set_header 透传原始路径信息
如果后端需要知道原始请求路径(比如用于日志、鉴权或生成跳转地址),不能只靠重写路径,还要通过请求头传递原始信息。
- 加一行:
proxy_set_header X-Original-URI $request_uri;
后端就能读到未重写的完整请求(含 query string) - 若需透传路径段变量,也可自定义头:
proxy_set_header X-Api-Version $1;(前提是$1在当前块内有效) - 注意:
$1等捕获变量只在定义它的location块内可用,不能跨块引用
避免常见陷阱
正则提取路径变量看似简单,但容易踩坑:
-
贪婪匹配误吞路径:用
.*可能跨段匹配,推荐[^/]+或[^?#]+限定范围,例如^/app/([^/]+)/([^/]+)/?$ -
proxy_pass 不识别 $1:写成
proxy_pass http://backend/$1;会报错或静默失效,必须先rewrite再转发 -
末尾斜杠影响匹配:
location /api/和location /api行为不同,前者自动去除前缀再拼接,后者原样转发,建议统一用location ~ ^/api/(.*)$显式控制











