应使用 req.Header.Get("Key") 安全读取请求头,它忽略大小写、返回空字符串而非 panic,并自动合并多值;需原始多值时用 req.Header["Key"] 并先判空;Cookie 等特殊字段不存于 Header 映射中,须用 r.Cookies() 或 r.Header"Cookie"。

如何从 *http.Request 中安全读取请求头字段
Go 的 net/http 包中,请求头以 http.Header 类型存在,本质是 map[string][]string。直接用 req.Header["User-Agent"] 可能 panic(如果 key 不存在且你忘了判空),更推荐用 req.Header.Get("User-Agent") —— 它返回 "" 而非 panic,且自动合并同名多值(如多个 Cookie 头会以逗号分隔)。
-
Get()忽略大小写,req.Header.Get("accept-encoding")和req.Header.Get("Accept-Encoding")效果一致 - 若需原始多值(例如解析多个
Set-Cookie),用req.Header["Set-Cookie"],但务必先检查 key 是否存在:if vals, ok := req.Header["X-Forwarded-For"]; ok { ... } - 注意:客户端可能发送重复 header(如两个
Authorization),Get()只返回第一个值;需要全部时必须用 map 访问
如何在中间件中修改或添加响应头
修改响应头必须在 WriteHeader() 或首次调用 Write() 之前完成,否则会被忽略。常见错误是想在 handler 结束后“补”一个 X-Response-Time,结果无效。
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// ✅ 此处可安全写入 Header
w.Header().Set("X-Process", "go-server")
w.Header().Add("X-Frame-Options", "DENY")
// 注意:不能在这里调用 w.WriteHeader() 或 w.Write() 前就 return
next.ServeHTTP(w, r)
// ⚠️ 此处再调用 w.Header().Set(...) 已无效果
log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
})
}
-
Set()覆盖已有值,Add()追加新值(适用于允许重复的 header,如Warning) - 若需删除某个 header,用
w.Header().Del("X-Deprecated") - 所有 header 修改对下游中间件可见,但不可逆(无法“撤回”已
Add()的值)
为什么 r.Header.Get("Cookie") 返回空,但 r.Cookies() 能拿到?
因为 Cookie 是 HTTP/1.1 特殊处理的 header:标准库在解析请求时,会将原始 Cookie: a=1; b=2 拆解并缓存为 []*http.Cookie,但**不会写回 r.Header 映射中**。所以 r.Header.Get("Cookie") 总是空字符串。
- 要读取 cookie 值,优先用
r.Cookies()(返回所有)或r.Cookie("session_id")(按名查找) - 若需原始 Cookie 字符串(比如透传给下游服务),应改用
r.Header["Cookie"](注意是 slice,取[0])或更稳妥地:遍历r.Header找 key 等于"Cookie"(忽略大小写)的项 - 同理,
Content-Length、Host等字段也可能被标准库提取并用于内部逻辑,不一定完整保留在r.Header中
如何正确处理大小写敏感的自定义 header
虽然 http.Header 内部用规范化的 key(首字母大写,如 "X-Request-Id")存储,但 Go 允许你用任意大小写访问 —— 底层通过 textproto.CanonicalMIMEHeaderKey 转换。不过,如果你依赖外部系统(如 Nginx、Envoy)注入的 header 名为 x-request-id,它仍能被 req.Header.Get("X-Request-Id") 正确匹配。
立即学习“go语言免费学习笔记(深入)”;
- 不建议手动调用
textproto.CanonicalMIMEHeaderKey,除非你在实现底层协议解析 - 若需严格区分大小写(极少见),应直接操作
req.Headermap 并遍历 key,但违背 HTTP 规范,多数代理会重写 header 名 - 生产环境建议统一使用标准格式命名自定义 header:以
X-开头,单词驼峰(X-Trace-Id),避免下划线或全小写
Cookie 字段的特殊性 —— 一个在响应写出前漏掉 Set-Cookie,一个因误用 Get("Cookie") 导致鉴权失败,都是高频线上问题。










