Go 的 net/http 默认自动跟随重定向,需设 CheckRedirect 为 nil 或返回 http.ErrUseLastResponse 来禁用;再通过状态码和 Location 头识别跳转,手动解析并安全处理跳转链以防循环。

Go 的 net/http 默认会自动跟随 HTTP 重定向(301、302、307、308),但很多时候你需要手动控制跳转行为——比如记录跳转链、避免无限重定向、区分临时与永久跳转,或在重定向时修改请求头。关键在于禁用默认重定向策略,并自己处理响应状态码。
禁用默认重定向并捕获跳转响应
默认的 http.Client 会自动处理重定向,要拿到原始 301/302 响应,需设置 CheckRedirect 字段为 nil 或返回错误:
- 设为
nil:完全禁用自动跳转,每次遇到重定向都直接返回响应 - 返回
http.ErrUseLastResponse:告诉客户端“别继续跳,把这次响应给我”
示例:
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse // 拦截所有重定向
},
}
识别并解析重定向响应
发起请求后,检查响应状态码和 Location 头即可判断是否为重定向:
立即学习“go语言免费学习笔记(深入)”;
- 状态码为
301(Moved Permanently)或302(Found)等 - 响应头中存在
Location字段,值为跳转目标 URL - 注意:
301表示永久跳转,适合更新缓存或 SEO;302是临时跳转,不应缓存原 URL 映射
代码片段:
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusMovedPermanently || resp.StatusCode == http.StatusFound {
location := resp.Header.Get("Location")
if location != "" {
fmt.Printf("Redirect %d → %s\n", resp.StatusCode, location)
}
}
手动执行跳转(可选重试或改写请求)
拿到 Location 后,你可以构造新请求继续访问,同时保留或修改 Header、Body 等:
- 用
resp.Request.URL.ResolveReference安全解析相对路径跳转地址 - 若需携带原始 Cookie 或认证头,记得复制到新请求中
- 对 301 可考虑缓存跳转结果,后续直接访问新地址
示例解析跳转地址:
u, err := resp.Request.URL.Parse(location)
if err != nil {
log.Fatal(err)
}
newReq, _ := http.NewRequest("GET", u.String(), nil)
newReq.Header = copyHeader(resp.Request.Header) // 自定义复制逻辑
安全处理重定向链(防循环/超长跳转)
真实场景中可能出现重定向环(A→B→A)或过长链路(>10 跳),建议加跳转计数限制:
- 在
CheckRedirect回调中维护跳转次数,超过阈值(如 10)即返回错误 - 记录已访问的 URL,检测重复跳转(尤其配合相对路径时)
- 对不可信来源的重定向,校验
Location是否为绝对 URL 且协议合法(如只允许https://)
简单计数实现:
redirectCount := 0
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
redirectCount++
if redirectCount > 10 {
return fmt.Errorf("too many redirects")
}
return nil // 允许跳转,由默认逻辑继续处理
},
}










