http.Get 无法控制重定向,因其底层使用 http.DefaultClient 且 CheckRedirect 为 nil,仅支持默认最多 10 次跳转;必须显式构造自定义 http.Client 并设置 CheckRedirect 函数才能干预跳转逻辑。

Go 的 http.Client 默认会自动跟随重定向,但你无法靠 http.Get 或默认客户端做任何干预 —— 必须显式构造带 CheckRedirect 的 http.Client 实例才能控制跳转逻辑。
为什么 http.Get 无法控制重定向
http.Get 底层用的是 http.DefaultClient,而它的 CheckRedirect 字段是 nil,意味着走标准策略:最多 10 次跳转,超限就报 stopped after 10 redirects。它不提供开关、不接受参数、也不允许你插手中间过程。
- 你不能给
http.Get传“跳转次数上限”或“只许跳同域名”这种配置 - 试图修改
http.DefaultClient.CheckRedirect是全局副作用,多 goroutine 下极易出错 - 错误信息如
net/http: invalid redirect policy往往是因为返回了非http.ErrUseLastResponse的自定义错误,但又没满足 Go 对重定向错误类型的校验
CheckRedirect 函数必须返回 http.ErrUseLastResponse 才能安全终止
很多人写 return errors.New("stop") 或 return fmt.Errorf("no redirect"),结果请求直接 panic 或返回包装后的 *url.Error,且 resp.Body 已被关闭 —— 这不是你想要的“拿到 302 响应体”。http.ErrUseLastResponse 是唯一被 Go 标准库识别为“中止但保留响应”的哨兵值。
- 返回
http.ErrUseLastResponse:函数立即退出,resp是上一次重定向响应(比如 302),resp.Body未关闭,可读取Location头 - 返回其他错误(哪怕只是
fmt.Errorf("too deep")):整个请求失败,resp为nil,你拿不到任何响应数据 - 返回
nil:继续发起下一次请求,via切片会增加一项
如何在重定向链中提取中间 URL 而不触发最终跳转
短链接展开、防付费墙、检测跳转目标是否内网 —— 这些场景都需要“看到即将跳去哪”,然后决定是否放行。关键不是阻止全部跳转,而是在某次跳转前检查 req.URL,再按需拦截。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
- 用
via[len(via)-1].URL获取上一次请求的原始 URL(即跳转来源) - 用
req.URL获取本次将要跳转的目标 URL(即Location值) - 常见判断逻辑:
if len(via) >= 5 { return http.ErrUseLastResponse }、if !strings.HasSuffix(req.URL.Host, "example.com") { return http.ErrUseLastResponse } - 注意:若需保留原始请求头(如
Authorization),默认不会透传,得手动写req.Header = via[0].Header.Clone()
别忘了 Timeout 和 Transport,CheckRedirect 不管网络耗时
CheckRedirect 只决定“跳不跳”,不决定“连不连得上”或“等不等得及”。如果重定向链里某个中间地址响应慢、DNS 解析卡住、或 TLS 握手超时,CheckRedirect 根本不会被调用 —— 请求早就挂在 Transport 层了。
- 务必为
http.Client设置Timeout(如10 * time.Second),否则可能无限等待 - 若需代理、自定义 TLS 配置或连接池调优,必须通过
Transport字段设置,而非依赖默认值 - 没有显式设置
Transport时,Client会用http.DefaultTransport,它对长连接、空闲连接复用有默认策略,但不适合高并发爬虫或调试场景
真正麻烦的从来不是“怎么写 CheckRedirect”,而是忘记它只在重定向发生时才执行 —— 网络超时、DNS 失败、TLS 错误、甚至 4xx/5xx 响应,都不会触发这个回调。所有非 3xx 的异常路径,都得靠 err 和 resp.StatusCode 单独处理。

















