因为Go标准库设计上主动过滤非安全头,Authorization和X-Forwarded-For等关键头被静默丢弃,必须在Director中手动补全:req.Header.Set("Authorization", req.Header.Get("Authorization"))、req.Header.Add("X-Forwarded-For", getClientIP(req))。

为什么 httputil.NewSingleHostReverseProxy 默认不透传 Authorization 和 X-Forwarded-For
Go 标准库的 httputil.NewSingleHostReverseProxy 会主动过滤掉“非安全头”,只保留 Connection、Content-Length、Transfer-Encoding 等少数几个头。像 Authorization、X-Forwarded-For、X-Real-IP 这类业务关键头,全被 silently drop 掉——不是 bug,是设计如此,为防头污染。
所以你看到后端收不到 Authorization,不是 Nginx 没传,也不是前端没发,而是 Go 代理在转发前就把它删了。
- 必须手动在
Director函数里补全原始请求头:req.Header.Set("Authorization", c.Request.Header.Get("Authorization")) -
X-Forwarded-For要追加而非覆盖:req.Header.Add("X-Forwarded-For", c.ClientIP())(注意:这里用c.ClientIP()是 Gin 提供的、已结合X-Forwarded-For和信任列表解析后的结果) - 别漏掉
X-Forwarded-Proto和X-Forwarded-Host,否则重定向或 OAuth 回调地址会变成http://127.0.0.1:8080
如何让 Director 正确重写 URL 并保留原始路径结构
直接用 path.Join(target.Path, c.Param("name")) 容易出错:它会把双斜杠变成单斜杠,且无法处理查询参数(?a=1&b=2)和 fragment(#section)。
正确做法是复用原始请求的 URL 结构体,只替换 Scheme 和 Host:
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
立即学习“go语言免费学习笔记(深入)”;
director := func(req *http.Request) {
req.URL.Scheme = target.Scheme
req.URL.Host = target.Host
// 不动 req.URL.Path 和 req.URL.RawQuery —— 它们已经包含完整路径+参数
// 如果需要 strip 前缀(比如 /proxy/),才手动改:req.URL.Path = strings.TrimPrefix(req.URL.Path, "/proxy")
}
- 若路由用
router.Any("/proxy/*name", handler),则c.Param("name")是/api/users?sort=asc(含 query),可直接赋给req.URL.Path - 若用
router.Any("/proxy/", handler),则c.Request.URL.Path已是完整路径,无需额外拼接 - 永远不要用
path.Join处理带 query 的路径,它会丢掉?后面所有内容
怎样在 ModifyResponse 中避免 502 错误并输出原始响应头
Go 反向代理不会自动删除 hop-by-hop 头(如 Connection、Keep-Alive、Proxy-Authenticate),这些头若原样透传给客户端,Nginx 或浏览器会拒绝解析,直接返回 502。
同时,你想看原始响应头(比如后端设的 Set-Cookie、Cache-Control),但默认行为是只拷贝部分头,其余被忽略。
- 必须显式设置
proxy.ModifyResponse,并在其中遍历resp.Header全量复制:for k, vv := range resp.Header { for _, v := range vv { writer.Header().Add(k, v) } } - 在复制前,先删掉 hop-by-hop 头:
resp.Header.Del("Connection")、resp.Header.Del("Keep-Alive")、resp.Header.Del("Proxy-Authenticate") - 如果后端用了
Content-Encoding: gzip,Go 代理默认不解压,ModifyResponse里也拿不到明文 body;如需 inspect body,得用httputil.NewSingleHostReverseProxy+ 自定义Transport+RoundTrip拦截,代价高,慎用
Gin 中调试反向代理时最容易忽略的三件事
你写完 Director 和 ModifyResponse,跑起来似乎没问题,但上线后突然发现登录失效、图片 404、WebSocket 断连——往往卡在这三个点上:
-
Host头没设:后端依赖r.Host构造回调地址,而 Go 代理默认把req.URL.Host设成目标地址,不是原始Host;必须加req.Header.Set("Host", c.Request.Host) - WebSocket 升级头没透传:
Upgrade和Connection: upgrade默认被过滤,需在Director后手动补:req.Header.Set("Upgrade", c.Request.Header.Get("Upgrade"))、req.Header.Set("Connection", c.Request.Header.Get("Connection")) - 信任 IP 列表为空:Gin 的
c.ClientIP()默认只信127.0.0.1,如果 Nginx 在另一台机器,X-Forwarded-For里的真实 IP 就被当成不可信段丢弃;得提前配置gin.SetMode(gin.ReleaseMode)并调用gin.Engine.ForwardedByClientIP = true,再用engine.TrustedPlatform = "X-Forwarded-For"显式指定信任头

















