Nginx 反代裸路径 301 带出后端端口的问题

Nginx 反代裸路径 301 带出后端端口的问题

记录时间:2026-09-16
环境:前置 Nginx(/AppHome/nginx2021,8443 块为外网入口,外层安恒 WAF + 华为云 CDN);后端 Nginx 节点(/AppHome/nginx,listen 56780)

一、问题现象

我想把 reverse-proxy-gzgd-https.conf 里 /gmet 的两段式配置(exact-match 301 + 斜杠块)合并成单个无斜杠 location,参考了网上两篇讲 absolute_redirect 的文章。改完访问 https://gz.example.cn/gmet,页面打不开,抓了下响应头:

HTTP/1.1 301 Moved Permanently
Location: http://gz.example.cn:56780/gmet/

Location 里带了后端端口 56780,协议还降级成了 http,外网只有 443 入口,这个地址根本访问不了。

二、排查过程

2.1 尝试 1:单块透传 + absolute_redirect off(无效)

网上两篇文章(文末参考资料)给的解法是 absolute_redirect off(1.11.8+),让 301 的 Location 从绝对地址变成相对路径。我按这个思路把 /gmet 改成:

location /gmet {
    absolute_redirect off;
    proxy_redirect off;
    proxy_set_header Host $host;
    ...
    proxy_pass http://10.0.0.112:56780;
}

上线还是老现象,这里我犯了两个判断错误:

  1. absolute_redirect off 只管 Nginx 自己发的重定向。自动补斜杠 301 只对 location /gmet/ 这种带斜杠的前缀触发,改成无斜杠 location 后 Nginx 根本不发 301,请求原样透传给了后端,这个 301 是后端发的
  2. proxy_redirect off 帮了倒忙,它关掉的正是”Nginx 改写后端 Location 头”的兜底行为。

2.2 尝试 2:块内 rewrite 补斜杠(推演通过,实测不行)

为了让后端永远收到带斜杠的路径,我改成:

location /gmet {
    rewrite ^/gmet$ /gmet/ break;
    ...
    proxy_pass http://10.0.0.112:56780;
}

推演是最优解(服务端改写、客户端零跳转),实测页面异常。gmet 前端是 hash 路由 SPA,入口是 https://gz.example.cn/gmet#/,fragment 不发给服务器;rewrite 后浏览器地址栏永远停在 /gmet,前端路由和资源解析就错了。这个应用必须真实跳转一次

2.3 翻到真凶:后端也是一台 Nginx

绕了一圈后看了后端节点的配置,发现后端根本不是应用服务器直接响应,也是一台 Nginx

server {
        listen       56780;
        server_name  localhost;

        location /gmet {
                alias   /AppHome/csptt/services/gmet;
                try_files $uri $uri/ /index.html;
                index  index.html index.htm;
        }
        ...
}

坏 301 的四个组成部分全部对上了:

Location 片段 来源
http: 后端 Nginx 自己的监听协议
gz.example.cn 前端 proxy_set_header Host $host 透传过去的 Host 头
:56780 port_in_redirect 默认 on,带出后端自己的 listen 端口
/gmet/ location /gmet + alias 指向目录,裸路径触发目录补斜杠 301

三、根因分析

裸路径 /gmet 从前端单块裸透传到后端 Nginx,后端 alias 指向的是一个目录,URI 没有以斜杠结尾,触发静态目录自动补斜杠 301。这个 301 是后端 Nginx 自产的,构造 Location 时协议取自己的 http、域名取透传的 Host 头、端口取自己的 56780。响应原样穿透前端(absolute_redirect 管不到,proxy_redirect off 还把改写关了)回给浏览器,浏览器去访问外网不存在的 56780 端口。

四、解决方案

从源头改后端。

4.1 后端节点

在 56780 的 server 块加一行:

server {
        listen       56780;
        server_name  localhost;
        absolute_redirect off;   # 目录补斜杠 301 改为相对路径 Location: /gmet/
        ...
}

后端仍然发 301,但 Location 变成相对的 /gmet/,前端原样透传,浏览器按当前地址栏的 origin(https://gz.example.cn)补全,fragment 保留,地址栏落到 /gmet/#/。

前提是后端 Nginx 版本 >= 1.11.8,先 nginx -V 确认。这个 server 块是 56780 独立端口,影响面可控。

4.2 前端节点

/gmet 改成单块裸透传:

location /gmet {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Scheme $scheme;
    proxy_set_header REMOTE-HOST $remote_addr;
    proxy_set_header X-Forwarded-Port $server_port;

    proxy_pass http://10.0.0.112:56780;

    access_log /AppLogs/ngx_acc_logs/gmet.gz.example.cn.log.json aka_logs;
}

proxy_redirect 保持默认(不写):相对 Location 不匹配改写规则、原样透传;万一后端哪天又回绝对地址,default 模式还能按 proxy_pass 地址前缀兜底改写。

各配置项说明:

配置项 说明
proxy_set_header Host $host 透传原域名,后端 301 的 Location 用它拼域名
proxy_set_header X-Real-IP $remote_addr 透传客户端真实 IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for 追加代理链 IP
proxy_set_header X-Forwarded-Proto / X-Forwarded-Scheme $scheme 告知后端原始协议(当前静态 Nginx 用不上,留作以后换应用服务)
proxy_set_header REMOTE-HOST / X-Forwarded-Port $remote_addr / $server_port 历史兼容透传
proxy_pass http://10.0.0.112:56780 不带 URI 部分,原样透传请求路径
access_log aka_logs 独立按业务切分的 JSON 访问日志

五、验证

后端生效验证(后端节点上):

curl -i http://10.0.0.112:56780/gmet

预期输出:

HTTP/1.1 301 Moved Permanently
Location: /gmet/

实测返回的就是纯相对路径,无域名无端口。

前端部署后待验证:同步配置 + nginx -t + nginx -s reload,华为云 CDN 刷新 https://gz.example.cn/gmet(测试期的坏 301 可能被边缘缓存),外网 curl 确认 Location: /gmet/,浏览器无痕访问 /gmet#/ 应落到 /gmet/#/。

六、注意事项

  • 单块无斜杠 location 会连 /gmetxxx 这类粘连前缀一起匹配,透传给后端后 404,实际无碍。
  • 后端若撤销 absolute_redirect off,前端单块会复现坏跳转,两侧存在耦合,前端配置处已加注释说明。

七、参考资料

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇