http.Setcookie设不上主因是响应头已写入,必须在w.WriteHeader或w.Write前调用;需显式设置Path、HttpOnly等字段;反向代理可能过滤Set-Cookie头;gorilla/sessions需复用store实例防连接泄漏;Domain设为.example.com才跨子域共享,localhost禁用带点domain。

Go 的 http.SetCookie 为什么设不上?
常见现象是调用 http.SetCookie 后浏览器没收到 Cookie,或值为空。根本原因通常是响应头已写入(即 ResponseWriter 已被刷出),此时再调用 SetCookie 无效。
关键点:必须在 w.WriteHeader 或任何 w.Write 之前设置 Cookie。
- 检查是否提前调用了
fmt.Fprintf(w, ...)或json.NewEncoder(w).Encode(...)—— 这些会隐式触发 header 写入 -
http.SetCookie不会自动设置Path和HttpOnly,不显式指定会导致前端 JS 可读、路径不匹配 - 如果使用反向代理(如 nginx),需确认它未过滤
Set-Cookie头或修改Domain
cookie := &http.Cookie{ Name: "session_id", Value: "abc123", Path: "/", HttpOnly: true, Secure: true, // 仅 HTTPS MaxAge: 3600, } http.SetCookie(w, cookie)
用 gorilla/sessions 管理 Session 的最小可行配置
Go 标准库不提供 Session 抽象,gorilla/sessions 是最常用且稳定的第三方方案。它本质是把 session 数据加密后存为 Cookie,或配合 store(如 redis)做服务端存储。
直接用 CookieStore 最轻量,但注意:所有 session 数据都经加密后塞进客户端 Cookie,体积不能超 4KB,且密钥一旦泄露可伪造 session。
立即学习“go语言免费学习笔记(深入)”;
- 密钥必须固定且足够长(推荐 32 字节以上),重启服务不能变;用
securecookie.GenerateRandomKey(32)初始化一次后硬编码 - 若启用
HttpOnly(默认开启),前端 JS 无法读取session_id,避免 xss 泄露 - 不要在 handler 中复用同一个
*sessions.Session实例跨 goroutine,每次调用store.Get(r, "mysession")都应视为新实例
var store = sessions.NewCookieStore([]byte("your-32-byte-secret-key-here")) func handler(w http.ResponseWriter, r *http.Request) { session, _ := store.Get(r, "mysession") session.Options = &sessions.Options{ Path: "/", MaxAge: 3600, HttpOnly: true, Secure: true, } session.Values["user_id"] = 123 session.Save(r, w) }
Session 存 Redis 时 redisstore 的连接泄漏风险
用 github.com/gorilla/sessions/redis 时,若初始化 redisstore.NewRediStore 次数过多(比如每个请求都 new 一个),会创建大量未复用的 Redis 连接,很快耗尽连接池或触发 too many open files。
正确做法是全局复用一个 *redisstore.RediStore 实例,并确保底层 *redis.Client 也复用(推荐用 redis.NewClient 单例)。
- 不要传
nil给NewRediStore的 client 参数——它会内部新建 client,且不暴露关闭方式 - 若用
redis.ClusterClient,需改用redisstore.NewRediStoreWithCluster,否则报错Interface conversion: redis.Cmdable is not redis.Client - Session ID 默认由 store 生成,无需手动 set;但若自定义 ID,必须确保唯一性,否则并发请求可能覆盖彼此的 session 数据
跨子域名共享 Cookie 的 Domain 设置陷阱
想让 app.example.com 和 api.example.com 共享同一份 session,必须在 http.Cookie.Domain 中设为 .example.com(开头带点)。但这个点不是可选的——漏掉就会失败。
更隐蔽的问题是:若当前请求 Host 是 localhost:8080,设 Domain: ".localhost" 会被浏览器拒绝(RFC 6265 明确禁止对 localhost 使用带点 domain)。
- 开发环境用
127.0.0.1替代localhost,然后设Domain: "127.0.0.1"(不加点) - 生产环境务必验证
Domain值与实际 Host 匹配,大小写敏感,且不能包含端口 -
SameSite默认是Lax,若需跨站提交表单携带 Cookie,得显式设为SameSite: http.SameSiteNoneMode,同时Secure: true必须开启
Session 的加密密钥、Redis 连接生命周期、Domain 语义细节——这三个地方出问题,基本占了线上状态管理故障的八成。别信“设了就完事”,每个环节都要对着 RFC 和浏览器 DevTools 的 Application → Cookies 面板逐项核对。