用 req.Header.Get("Key") 读取请求头时无需关心大小写,因 Go 已自动标准化为小写;设置响应头须在 Write 前调用 w.Header().Set();Cookie 需用 req.Cookies() 解析而非 req.Header.Get("Cookie");修改请求头需新建 *http.Request。

如何用 http.Request.Header 读取客户端请求头
Go 的 http.Request 对象里,Header 是一个 http.Header 类型(本质是 map[string][]string),所有请求头字段都以小写键名存储,无论客户端发来的是大写还是混合大小写 —— Go 在解析时已自动规范化。
常见错误是直接用原始大小写去查,比如 req.Header.Get("Content-Type") 返回空,因为实际键名是 "content-type"。正确做法是始终用小写键名,或直接用 Get 方法(它内部做了标准化):
contentType := req.Header.Get("Content-Type") // ✅ Get 自动处理大小写
accept := req.Header.Get("accept") // ✅ 等效
userAgent := req.Header.Get("User-Agent") // ✅ 同样有效
注意:Get 只返回第一个值;若需全部值(如多个 Cookie 头),要用 req.Header["cookie"] 获取切片。
如何用 http.ResponseWriter.Header().Set() 设置响应头
设置响应头必须在调用 Write 或 WriteHeader 之前完成,否则会 panic 并打印 http: superfluous response.WriteHeader call 错误。
立即学习“go语言免费学习笔记(深入)”;
Set 会覆盖已有同名头;Add 则追加新值(适用于允许重复的头,如 Set-Cookie);Del 删除指定头:
w.Header().Set("Content-Type", "application/json; charset=utf-8")w.Header().Add("Set-Cookie", "sessionid=abc123; HttpOnly; Path=/")w.Header().Del("X-Powered-By")
特别注意:Content-Type 和 Status 有快捷方式:w.Header().Set("Content-Type", ...) 等价于 w.Header().Set("content-type", ...),但更推荐显式设置,避免和 http.Error 或 json.NewEncoder(w).Encode(...) 的隐式行为冲突。
为什么 req.Header.Get("Cookie") 返回空,而 req.Cookies() 可用
Cookie 头在 HTTP 协议中是特殊字段:它可能被拆分成多行(虽然不常见),且 Go 的 http.Request 解析器默认不将其合并进 Header 映射,而是交由 req.Cookies() 统一解析并返回 []*http.Cookie。
所以不要依赖 req.Header.Get("Cookie") 获取原始字符串 —— 它可能为空或不完整。正确方式是:
cookies, err := req.Cookies()
if err != nil {
http.Error(w, "bad cookie", http.StatusBadRequest)
return
}
for _, c := range cookies {
if c.Name == "sessionid" {
// 处理 sessionid
}
}
如果真需要原始 Cookie 字符串(例如做签名验证),应使用 req.Header["cookie"](注意小写)并手动拼接:strings.Join(req.Header["cookie"], "; "),但多数场景下直接用 Cookies() 更安全。
自定义中间件中修改请求头的注意事项
HTTP 请求头是只读的 —— 你不能通过 req.Header.Set(...) 修改传入的 *http.Request,因为这不会影响下游 handler 看到的请求(除非你显式构造新 request)。常见误区是以为改了 header 就能透传给后端服务。
若需转发请求并添加/覆盖头(如反向代理场景),必须用 http.NewRequest 创建新 request,并调用 req.Header.Set 或 req.Header.Add:
newReq, _ := http.NewRequest(req.Method, targetURL, req.Body)
newReq.Header = make(http.Header)
for k, vv := range req.Header {
for _, v := range vv {
newReq.Header.Add(k, v)
}
}
newReq.Header.Set("X-Forwarded-For", req.RemoteAddr)
记住:原 req.Header 是只读视图,任何修改都不会改变其底层映射;只有新建 request 并显式赋值 Header 才有效。这也是很多自定义 middleware 出现 header 丢失的根本原因。










