http.redirect 是最安全的默认选择,但其默认 302 跳转会将 post 变为 get 导致数据丢失;需保持方法时须手动设状态码与 location 头且不写 body,注意缓存、协议差异及循环重定向防护。

Go HTTP重定向:用 http.Redirect 还是手动设状态码?
直接结论:http.Redirect 是最安全的默认选择,但它的默认行为(http.StatusFound + Location 头 + body)在某些场景下会出问题。
常见错误现象:前端发起 POST 请求后被 http.Redirect 302 跳转,结果变成 GET,原始数据丢失;或 API 客户端收到重定向却没自动跟随,返回空响应。
- 如果需要保持原请求方法(比如 POST → POST),不能用
http.Redirect,得手动写:w.WriteHeader(http.StatusMovedPermanently)+w.Header().Set("Location", "/new-path"),且不写任何 body -
http.Redirect的第四个参数(code)别硬写数字,用http.StatusTemporaryRedirect这类常量,避免语义混淆 - 注意浏览器缓存:301/308 会被缓存,测试时清缓存或换无痕窗口,否则改了代码也看不到效果
Go 中实现反向代理转发:为什么 httputil.NewSingleHostReverseProxy 不能直接用?
它能跑起来,但上线就出问题——不是功能缺陷,而是默认配置忽略关键现实约束。
使用场景:把 /api/ 前缀请求转发到后端服务,同时透传 Host、Cookie、TLS 信息。
立即学习“go语言免费学习笔记(深入)”;
- 默认不重写
Host头,后端收不到原始域名,需在Director函数里显式设置:req.Host = url.Host - 默认不透传客户端真实 IP,后端
X-Forwarded-For为空,得自己加:req.Header.Set("X-Forwarded-For", getClientIP(req)) - 如果后端是 HTTPS,
url.Scheme必须是"https",否则NewSingleHostReverseProxy内部会降级成 HTTP 连接,报malformed HTTP response
重定向循环检测:Go HTTP Server 自己不会做
Go 的 http.ServeMux 和 http.Handler 层完全不关心你是不是在 redirect 到自己,循环全靠人盯。
典型触发条件:路径匹配逻辑有重叠,比如 /v1/ 重定向到 /api/v1/,而 /api/ 又被另一个 handler 拦截并重定向回 /v1/。
- 加日志是最简单有效的手段:在每次重定向前打一行
log.Printf("redirect %s → %s", req.URL.Path, target) - 用
req.Header.Get("X-Forwarded-For")或自定义 header(如X-Redirect-Hop)计数,超过 3 跳直接 500,防止雪崩 - 测试时别只测单次跳转,用
curl -v看完整跳转链,或写个简单脚本模拟多层跳转
HTTP/2 下重定向与代理的隐藏差异
Go 1.8+ 默认启用 HTTP/2,但它对重定向响应头和代理转发的处理比 HTTP/1.1 更严格。
表现就是:某些看似正常的重定向,在 Chrome 或 curl HTTP/2 模式下失败,报 net/http: HTTP/1.x transport connection broken 或静默卡住。
- 确保重定向目标 URL 的 scheme 明确(
https://或http://),不要用相对路径,HTTP/2 不支持Location: /new这种写法 - 反向代理时,如果后端不支持 HTTP/2,而前端 client 强制走 HTTP/2,Go 的
ReverseProxy会自动降级,但部分 header(如Te: trailers)可能引发后端解析异常,建议在Transport中禁用 HTTP/2:&http.Transport{ForceAttemptHTTP2: false} - 所有重定向响应必须有
Content-Length: 0或明确的Transfer-Encoding,HTTP/2 对空 body 的编码更敏感,漏掉会导致连接复用异常
重定向和转发看着简单,真正难的是边界情况:客户端怎么发、中间网关怎么拦、后端怎么认、协议版本怎么适配。这些地方不提前想清楚,线上就只能靠日志一行行扒。










