必须同步设置 req.URL.Host 和 req.Host,否则后端因 Host 头不匹配而 404;需配置 Transport 超时与连接池参数防止 goroutine 泄漏;须手动添加 X-Forwarded-For 透传真实 IP;HTTPS 后端需显式处理证书校验。

Director 必须重写 req.URL.Host 和 req.Host
默认的 httputil.NewSingleHostReverseProxy 只改了 req.URL.Host,但不碰 req.Host。如果后端依赖 Host 头做路由(比如 Nginx 的 server_name、Spring Cloud Gateway 的 host 路由),就会 404 或跳转错乱。
常见错误是只设 req.URL.Host = "backend:8080",却漏掉 req.Host —— 后端收到的仍是客户端原始 Host,不是你期望的目标地址。
- 必须同时设置:
req.URL.Host = backendURL.Host和req.Host = backendURL.Host - 若后端监听
127.0.0.1:8080,别用localhost:8080:本地 hosts 或 DNS 缓存可能导致解析失败或连接被拒 - HTTPS 后端还要同步设
req.URL.Scheme = "https",否则 RoundTrip 默认走 HTTP 协议
Transport 不配就等于裸奔
没设 proxy.Transport 时,ReverseProxy 默认用 http.DefaultTransport,它没有超时、无连接数限制、空闲连接永不释放。并发稍高就卡死,跑几天后 goroutine 数破万、内存持续上涨。
这不是代码逻辑问题,是底层连接池失控。
立即学习“go语言免费学习笔记(深入)”;
在 Golang 中使用 samber/hot 进行内存缓存,支持 LRU、LFU、TinyLFU、W‑TinyLFU、S3FIFO、ARC、TwoQueue、SIEVE、FIFO 等淘汰算法,提供 TTL、缓存加载器及分片功能。
- 至少设三项:
MaxIdleConns、MaxIdleConnsPerHost(建议都设为 100)、IdleConnTimeout(如30 * time.Second) - 必须加超时:
ResponseHeaderTimeout(等响应头)、ReadTimeout(读整个响应体)——后者最关键,能中断卡在大文件传输中的连接 - 别复用全局
http.DefaultClient;每个ReverseProxy实例应配独立http.Transport
透传真实客户端 IP 要手动补 X-Forwarded-For
ReverseProxy 默认不加任何 X-Forwarded-* 头。后端看到的 RemoteAddr 永远是代理本机地址(如 127.0.0.1:56789),不是用户真实 IP。
这不是 bug,是设计:Go 不自动注入头,得你自己决定怎么传、传什么。
- 在
Director函数里加:req.Header.Set("X-Forwarded-For", clientIP(req)) -
clientIP()应优先取X-Real-IP,fallback 到req.RemoteAddr并去掉端口(如"192.168.1.100:12345" → "192.168.1.100") - 注意不要重复添加:如果上游已设
X-Forwarded-For,应拼接而非覆盖,避免形成"1.1.1.1, 127.0.0.1, 127.0.0.1"这种链
HTTPS 后端连自签名证书都要显式跳过校验
用 httputil.NewSingleHostReverseProxy 代理 HTTPS 后端时,默认 Transport 会严格校验证书。内网自签、用 IP 访问、SNI 不匹配,全都会报 x509: certificate signed by unknown authority。
这不是代理写错了,是 TLS 客户端行为。
- 测试/内网环境可设:
TLSClientConfig.InsecureSkipVerify = true - 若证书合法但域名不匹配(比如用
https://10.0.1.100访问),还需指定TLSClientConfig.ServerName = "backend.example.com" - 生产环境禁用
InsecureSkipVerify:它等于放弃 TLS 的身份认证能力,风险不可控
实际部署时,最容易被忽略的是 ReadTimeout 和 req.Host 同步——前者导致代理堆积 goroutine,后者让后端路由失效,两个问题都很难一眼定位。

















