Go的http.Client默认自动跟随301/302跳转(最多10次),通过CheckRedirect字段控制逻辑;禁用需返回http.ErrUseLastResponse,自定义须校验Host等并避免return nil。
Go 的
默认会自动跟随 301/302 跳转
除非你显式禁用,
和任何新构造的
都会自动处理重定向(最多 10 次),并返回最终响应,而不是跳转响应本身。这在多数场景下是便利的,但也会掩盖真实跳转链、干扰调试,或导致意外访问第三方地址。
关键控制点在
字段——它是个函数,决定“遇到跳转时该不该继续”。默认值是
,内部做了次数限制和简单校验。
想完全禁用跳转?把
设为
想自定义跳转逻辑?比如只允许同域名跳转,就在这里做
校验
注意:返回
会让 client 直接返回跳转响应(即 status 302 的 resp),而不是继续请求 Location
为什么
返回 error 就算“终止”
Go 的 HTTP client 把重定向当作一个「可能失败的流程」,不是分支逻辑。只要
返回非 nil error,client 就认为“用户拒绝继续”,立刻停止跳转,并把当前跳转响应(如 302)作为最终
返回。
常见错误现象:
函数里忘了 return,或者写了
—— 这会导致 panic:
。Go 强制要求必须明确返回 error 或
。
立即学习
“
go语言免费学习笔记(深入)
”;
正确写法只有两种:返回
(停在当前跳转),或返回其他 error(如
)
别用
,也别漏写 return
如果只是想记录跳转过程,仍需返回
或原样调用
手动实现跳转跟踪时要注意
和
当禁用自动跳转后,你得自己解析
并发起下一次请求。这时容易忽略原始请求的上下文是否被保留:
不会自动继承到新请求——比如带
或
的请求,跳转后要手动复制
默认是一次性的:如果原请求有 body(如 POST),且你打算在跳转后重发 body,得确保 body 可重读(例如用
包装)
307/308 跳转要求方法和 body 不变;301/302 则规范要求降级为 GET 且丢弃 body —— 但 Go 默认不区分,全按 301/302 处理,除非你手动判断状态码再决策
实际调试中,
的
和跳转可能互相干扰
如果你设置了
(默认行为),而跳转目标域名被代理规则匹配,client 会尝试走代理访问跳转后的地址。这可能导致:本地能直连的地址,跳转后却因走代理失败;或代理本身返回 302 导致嵌套跳转失控。
排查这类问题时,别只盯着
,先确认跳转 URL 是否真的被代理了:
临时设
排除代理干扰
用
对比原始请求和跳转 URL 的行为,看是否一致
注意:某些企业网络对
中的相对路径(如
)解析不一致,Go 会自动补全为绝对 URL,但中间代理可能不这么干
跳转逻辑看着简单,但一牵扯到认证头、body 重放、代理、相对路径补全,就很容易在第二三次跳转时突然失败——这时候往往不是代码写错了,而是没意识到 client 已经帮你做了太多事,而你又手动绕过了其中一部分。
http.Clienthttp.DefaultClienthttp.Client{}Client.CheckRedirecthttp.DefaultCheckRedirectCheckRedirectfunc(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }req.URL.Hosthttp.ErrUseLastResponseCheckRedirectCheckRedirect*http.ResponseCheckRedirectreturn nilpanic: net/http: CheckRedirect function returned nilhttp.ErrUseLastResponsehttp.ErrUseLastResponseerrors.New("redirect blocked")return nilhttp.ErrUseLastResponsehttp.DefaultCheckRedirectreq.Headerreq.Bodyresp.Header.Get("Location")req.HeaderAuthorizationUser-Agentreq.Bodybytes.NewReaderhttp.TransportProxyhttp.Transport.Proxy = http.ProxyFromEnvironmentCheckRedirectTransport.Proxy = http.ProxyURL(nil)curl -vLocation/new-path