Go net/http Client重定向时降级敏感请求头的处理方案
来源:17golang原创
时间:2026-09-15 21:53:24 110浏览 收藏
服务端返回 301、302 或 307 时,Go 的 http.Client 不只是换一个 URL:它还会决定哪些请求头进入下一跳。默认策略会保护跨不可信域名的敏感头,但同域、子域以及同一主机换 scheme 的边界并不等同于严格的 origin。要让重定向明确降级,做法是给 Client.CheckRedirect 配置一层 origin 判断,在边界变化时删除凭据头,再决定是否继续。
- 默认规则会对不匹配域名忽略
Authorization、WWW-Authenticate和Cookie,但子域与同主机换 scheme 仍可能保留敏感头。 CheckRedirect收到的是即将发送的请求和历史请求,可用最后一跳比较 scheme、host、端口。- 自定义的
X-API-Key不属于默认保护清单,跨 origin 时应由业务策略显式删除。
官方地址:https://pkg.go.dev/net/http。本文只讨论 Go net/http Client 的重定向请求头边界,不扩展到 HTTP 框架选型。
默认重定向规则为什么不等于严格同源
Client 跟随重定向时,会把初始请求的普通头带到后续请求;对敏感头,默认规则会在目标域名既不是原域名、也不是原域名的子域时忽略它们。例如 api.example.com 到 cdn.example.com 仍属于子域匹配,而到 other.example.net 则不会继续带凭据。
这个规则解决的是“不要把凭据发给完全无关域名”,却不是严格的 origin 策略:同一 host 从 HTTPS 跳到 HTTP,或者从一个端口跳到另一个端口,仍可能落在默认转发范围内。若业务把 scheme、端口也视为信任边界,就需要覆盖 CheckRedirect。

用 CheckRedirect 比较前后请求的 origin
回调里的 req 是即将发送的下一跳,via 按从旧到新的顺序保存已发请求。比较 via 最后一项与 req 的 scheme 和 host,可以得到比“同域或子域”更清晰的同源判定。
package main
import (
"net/http"
"strings"
)
// sameOrigin 只把 scheme、主机和端口都相同的地址视为同一信任边界。
func sameOrigin(a, b *http.Request) bool {
return strings.EqualFold(a.URL.Scheme, b.URL.Scheme) &&
strings.EqualFold(a.URL.Host, b.URL.Host)
}
// redirectPolicy 读取上一跳请求,再处理即将发送的下一跳请求。
func redirectPolicy(req *http.Request, via []*http.Request) error {
if len(via) == 0 {
return nil // 第一跳没有历史请求,不需要比较。
}
previous := via[len(via)-1]
if !sameOrigin(previous, req) {
// 跨 origin 时删除凭据;X-API-Key 是业务自定义敏感头。
req.Header.Del("Authorization")
req.Header.Del("Cookie")
req.Header.Del("X-API-Key")
}
return nil // 返回 nil 才允许 Client 继续跟随重定向。
}
var client = &http.Client{CheckRedirect: redirectPolicy}
这里的策略比默认行为更保守:子域跳转也会清理头,端口变化同样会清理。普通的 User-Agent、追踪 ID 等非凭据头可以继续沿用,但不要把“所有 Header 都安全”作为假设。
| 跳转情况 | 默认 Client | 上面策略 |
|---|---|---|
| 同域同 scheme 同端口 | 通常保留敏感头 | 保留 |
| 子域或端口变化 | 可能保留 | 清理敏感头 |
| 完全不同域名 | 忽略敏感头 | 清理敏感头 |
自定义 API 密钥要在回调里显式降级
默认保护清单并不知道业务自定义的 X-API-Key、租户签名或内部追踪凭据。只配置 Authorization 的删除是不够的,应该把所有真正代表身份或授权的头集中列入策略。Cookie 还要注意 Jar:有非空 Cookie Jar 时,客户端会在每次重定向前后更新和重新装配 Cookie,不能只依赖初始请求里的 Header。

如果业务根本不接受自动跳转,可以直接停止链路,让上层读取响应的 Location 再做白名单判断:
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
// 中文注释:到达重定向回调时不再自动发送下一跳,保留最后响应供调用方判断。
return http.ErrUseLastResponse
},
}
resp, err := client.Get("https://api.example.com/resource")
if err != nil {
// 中文注释:网络错误或客户端策略错误都要交给调用方处理。
return err
}
defer resp.Body.Close() // 中文注释:ErrUseLastResponse 返回的响应体由调用方关闭。
// 中文注释:此处可读取 resp.Location,并按业务白名单决定是否发起新请求。
307/308、响应体和重定向次数的检查清单
301、302、303 通常会把后续方法改成 GET;307、308 会保留原方法和请求体,但前提是 Request.GetBody 可用。上传类请求尤其要确认请求体能否重放,不要因为清理了 Header 就误以为重定向链已经安全。
- 只允许预期的 scheme、host 和端口,或在
CheckRedirect中遇到异常直接返回错误。 - 将
Authorization、Cookie、API 密钥、租户签名等业务凭据列成明确清单。 Do成功返回后读取并关闭resp.Body;使用ErrUseLastResponse时也一样。- 复用同一个
http.Client,并为重定向链保留最大次数控制,避免无限跳转消耗请求预算。
相关问题
为什么跳到子域时 Authorization 还在?
这是 Go 默认规则的一部分:同域或子域匹配时允许转发敏感头。如果子域不是同一信任边界,应在 CheckRedirect 中主动删除。
删除 Cookie Header 就能清掉 Cookie 吗?
不一定。配置 Cookie Jar 后,Jar 可能根据响应重新装配匹配域名的 Cookie;需要同时限制允许的目标 origin,必要时不用 Jar 或停止自动重定向。
CheckRedirect 返回普通 error 后响应体谁关闭?
客户端文档规定策略错误返回的上一响应体已关闭;如果返回 ErrUseLastResponse,最后响应体保持打开,必须由调用方关闭。
-
Golang · Go教程 | 37分钟前 | 连接池 · Go教程 · net/http · HTTP客户端 · Transport · Go net/http Client Go Transport拆分 Go多上游HTTP客户端 Go连接池隔离 Go HTTP代理配置323 收藏
-
232 收藏
-
Golang · Go教程 | 1小时前 | 静态资源 · Go教程 · io/fs · embed.FS · go:embed · go:embed Go embed.FS fs.Glob glob目录 静态资源嵌入140 收藏
-
163 收藏
-
167 收藏
-
358 收藏
-
139 收藏
-
247 收藏
-
Golang · Go教程 | 2小时前 | 错误处理 · 流式处理 · Go教程 · io.Copy · io.MultiReader · Go 错误处理 io io.Reader io.Copy io.MultiReader 输入流421 收藏
-
148 收藏
-
123 收藏
-
Golang · Go教程 | 3小时前 | Go教程 · encoding/json · JSON解码 · 兼容升级 · Go encoding/json json.RawMessage json.Decoder 未知字段 兼容升级468 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习