必须显式重写req.url.scheme、req.url.host和req.url.path,否则默认转发失败;需补x-forwarded-for等头透传真实ip;transport须自定义连接池与超时,多后端应分设独立reverseproxy实例。

httputil.NewSingleHostReverseProxy 默认不改 req.URL,直接用会转发失败——这是最常踩的坑。
Director 函数必须显式重写 URL 三要素
反向代理不会自动把请求发到你 url.Parse 传进去的那个地址。它只负责调用 Director 函数,然后按修改后的 req.URL 发送。漏掉任意一项,大概率是 502 Bad Gateway 或后端收到空路径。
-
req.URL.Scheme:后端是https就得设"https",否则默认走http -
req.URL.Host:必须设成目标服务的host:port,不能依赖构造时传的*url.URL -
req.URL.Path:推荐用singleJoiningSlash(target.Path, req.URL.Path)拼接,避免双斜杠或丢路径
不补 X-Forwarded-* 头,下游就看不到真实客户端 IP
httputil.ReverseProxy 默认不加任何 X-Forwarded-* 头。如果你的后端靠 X-Forwarded-For 做限流、日志或地域判断,它看到的永远是网关本机地址。
- 用
req.RemoteAddr解析出客户端 IP,过滤掉私有地址(如127.0.0.1、192.168.x.x)再填入X-Forwarded-For -
X-Forwarded-Proto要根据req.TLS != nil判断设"https"还是"http" -
X-Forwarded-Host直接设req.Host即可 - 注意:
Header.Set是覆盖,Header.Add才是追加;X-Forwarded-For应该用Add
Transport 不配,高并发下很快打满文件描述符
NewSingleHostReverseProxy 创建的实例,其 Transport 字段初始为 nil,最终 fallback 到 http.DefaultTransport。而它的默认值 MaxIdleConnsPerHost = 2,压测时几秒就耗尽连接。
- 必须显式配置
proxy.Transport,至少调大两个参数:MaxIdleConns和MaxIdleConnsPerHost - 如果后端支持 HTTP/2,要加
ForceAttemptHTTP2: true,否则降级成 HTTP/1.1 - 记得设
IdleConnTimeout和ResponseHeaderTimeout,别让超时全走默认的 30 秒
多个后端怎么路由?别硬塞进一个 proxy 实例
一个 ReverseProxy 实例只能代理到单个主机。想分发到 /api/user 和 /api/order 两个不同后端,不能靠改 Director 里判断路径来“复用”同一个 proxy——那样会污染状态、难以维护。
- 每个后端单独建一个
ReverseProxy实例,各自配好Director和Transport - 用
http.ServeMux或httprouter做前缀匹配,再分别调用对应 proxy 的ServeHTTP - 如果需要动态加载路由,把后端配置存 JSON/YAML,启动时解析并注册 handler,而非硬编码
真正难的不是写通一个转发,而是让每个 Director 安全地处理路径拼接、每个 Transport 稳定复用连接、每条 X-Forwarded-For 不被伪造——这些细节不手动控制,线上一压就露馅。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











