
反向代理核心是 ReverseProxy,不是自己拼接请求
很多人一上来就用 http.Client 手动发请求、读响应、拷贝 header,结果卡在 chunked 编码、连接复用、upgrade 头处理上。Go 标准库的 httputil.NewSingleHostReverseProxy 已经封装了这些细节,直接用它,别绕路。
常见错误现象:502 Bad Gateway 或响应体为空,往往是因为手动处理 response body 时没调用 resp.Body.Close(),或漏传 Connection、Upgrade 等 hop-by-hop header。
-
ReverseProxy默认不转发Connection、Keep-Alive、Proxy-Authenticate等 hop-by-hop header,这是对的,别强行加回去 - 若后端支持 WebSocket,必须显式设置
Director函数并透传Upgrade和Connectionheader - 修改请求路径(比如去掉前缀)要在
Director里改req.URL.Path和req.URL.RawPath,否则 URL 解析可能出错
Director 函数里改请求地址,别碰 req.Host 除非真需要
Director 是反向代理唯一可控的请求改写入口,但很多人误以为要手动设 req.Host = "backend:8080"。其实 NewSingleHostReverseProxy 已经把目标 host 写进 req.URL,req.Host 应该留给下游服务判断“用户最初访问的是哪个域名”——也就是保留原始 Host。
使用场景:前端请求 https://api.example.com/v1/users,网关需转发到 http://127.0.0.1:8000/v1/users,且后端依赖 Host 做路由或证书验证。
立即学习“go语言免费学习笔记(深入)”;
- 正确做法:只改
req.URL.Scheme、req.URL.Host、req.URL.Path;保持req.Host不变(即客户端传来的值) - 如果后端真要你伪造 Host(比如多租户隔离),再赋值
req.Host = "tenant-backend",但这是例外,不是默认 - 注意
req.URL.Path末尾斜杠:若原始路径是/api/,而你改成/,又没同步处理req.URL.RawPath,某些带特殊字符的路径会解码异常
超时控制必须在 Transport 层设,Timeout 字段没用
ReverseProxy 本身没有 Timeout 字段,网上很多代码写 proxy.Timeout = 30 * time.Second 是无效的——那字段根本不存在。真正起作用的是底层 http.Transport 的三个超时:连接、读、写。
性能影响:不设超时,后端卡住时 goroutine 会一直挂起,网关内存和连接数缓慢上涨,最终雪崩。
- 必须用
&http.Transport{...}替换默认 transport,并设IdleConnTimeout(防连接池积压)、ResponseHeaderTimeout(防 header 卡住)、ReadTimeout(防大响应体慢吞吞) - 不要只设
Timeout(已废弃),也不要只设DialTimeout—— 它只管建连,不管后续卡顿 - 若后端是 gRPC over HTTP/2,还得额外设
ForceAttemptHTTP2: true和TLSClientConfig
日志和错误处理不能只靠 log.Printf,得捕获 RoundTrip 错误
默认情况下,ReverseProxy 把后端错误(如连接拒绝、超时)转成 502 返回给客户端,但不暴露具体原因。调试时只能看到 “502”,不知道是 DNS 失败、TLS 握手超时,还是后端进程挂了。
容易踩的坑:有人在 Director 里 panic,结果整个 http handler 崩溃;或者用 defer log.Println() 捕获不到 transport 层错误。
- 正确方式:包装
http.Transport.RoundTrip,在返回前检查err != nil,记录完整错误类型和地址 - 例如:
net.OpError表示网络问题,net/http.http2noCachedConnError表示 HTTP/2 连接复用失败 - 别在
Director中做耗时操作(如查 DB、调外部 API),它在主请求 goroutine 中执行,阻塞整个代理流程
最常被忽略的其实是 ReverseProxy 对 Trailer header 和分块传输的隐式处理——如果你的后端用 Transfer-Encoding: chunked 且带 trailer,不升级到 Go 1.19+,某些 trailer 字段会丢。这事不报错,也不告警,只悄悄消失。










