Nginx中proxy_redirect不支持直接使用变量,需通过map预定义映射关系再引用,或用sub_filter替换响应内容,或借助nginx-lua模块实现真正基于变量的全自动重定向修正。

使用 proxy_redirect 结合 Nginx 变量实现全自动路径修正,核心在于让重定向响应头中的 Location、Refresh 等字段,能动态适配上游服务返回的原始路径与当前代理上下文之间的差异。Nginx 本身不支持在 proxy_redirect 指令中直接使用变量(如 $scheme、$host、$request_uri),但可通过变通方式达成“类自动”效果。
理解 proxy_redirect 的限制与替代思路
proxy_redirect 的语法只接受字面字符串或正则表达式,不能写成 proxy_redirect http://backend/ $scheme://$host/;。因此“全自动”需拆解为:先用 proxy_redirect off 关闭默认重写,再通过 sub_filter 或 map + proxy_redirect 组合模拟变量行为。
- 若上游返回
Location: http://api.example.com/v1/user,而你希望客户端跳转到https://your.domain.com/api/v1/user,单纯靠静态proxy_redirect需预先知道所有可能的 host 和 scheme - 真正灵活的方式是:用
map提前定义上游地址与对外地址的映射关系,再结合proxy_redirect引用这些 map 变量 - 更彻底的方案是禁用
proxy_redirect,改用sub_filter对响应头和响应体中的 URL 做实时替换(需开启sub_filter_types并注意二进制响应限制)
用 map + proxy_redirect 实现条件化路径映射
这是最推荐的“准自动”方式:把上游响应中的 host/port/scheme 映射为对外暴露的值,再在 proxy_redirect 中引用这些 map 变量。
示例配置:
map $upstream_http_location $redirect_host {
~^http://api\.internal(?::\d+)?/ $scheme://$host;
~^https://api\.internal(?::\d+)?/ https://$host;
default $scheme://$host;
}
map $upstream_http_location $redirect_prefix {
~^https?://[^/]+(/.*) $1;
default /;
}
location /api/ {
proxy_pass http://backend_api/;
proxy_redirect $upstream_http_location $redirect_host$redirect_prefix;
proxy_set_header Host $host;
}
说明:
- 第一个
map根据上游Location响应头内容,决定重定向时使用的 scheme+host - 第二个
map提取原始 Location 中的 path 部分(去掉协议和 host),避免硬编码路径前缀 -
proxy_redirect将上游完整 Location 替换为拼接后的目标地址,实现动态修正
用 sub_filter 替代 proxy_redirect(适用于复杂重写)
当需要修改响应体中的链接(如 HTML 中的 <a href="..."></a>)、或上游返回多个重定向头、或路径嵌套层级不确定时,sub_filter 更灵活。
- 启用
sub_filter需配合sub_filter_once off和sub_filter_types *(谨慎使用*,建议明确指定text/html text/css application/javascript) - 例如将响应中所有
http://api.internal/v1替换为/api/v1:sub_filter 'http://api.internal/v1' '/api/v1';<br>sub_filter_once off;
- 注意:sub_filter 不处理响应头,所以仍需配合
proxy_redirect off,并用add_header或 Lua 模块补全 Location 头(若需完全控制)
进阶:用 nginx-lua 模块实现真变量重定向
若已编译 Lua 支持(OpenResty 或 ngx_lua),可完全接管重定向逻辑:
location /api/ {
proxy_pass http://backend_api/;
proxy_redirect off;
header_filter_by_lua_block {
local location = ngx.var.upstream_http_location
if location and string.match(location, "^https?://") then
local new_loc = string.gsub(location, "https?://[^/]+", ngx.var.scheme .. "://" .. ngx.var.host)
ngx.header["Location"] = new_loc
end
}
}
该方式可任意解析、拼接、判断,真正实现基于变量的全自动修正,适合多租户、灰度路由等复杂场景。


















