Go默认启用SessionTicket但需服务端配置SessionTicketsKeys且客户端启用ClientSessionCache才实际生效;否则降级为完整握手,无法复用。

Go 的 crypto/tls 默认启用 SessionTicket,但**仅当服务端配置了密钥且客户端支持时才实际生效**;单纯不关不等于能复用,关键在服务端密钥管理和客户端缓存行为。
服务端必须显式设置 SessionTicketsKeys 才能启用 ticket 复用
Go 的 TLS server 默认不生成或保存 ticket 密钥,tls.Config 中的 SessionTicketsKeys 字段为空切片时,server 会拒绝 client 的 ticket 请求(降级为 full handshake)。即使 client 发了 ticket,server 也无密钥解密,只能新建会话。
-
SessionTicketsKeys是一个[]tls.SessionTicketKey切片,每个 key 包含 32 字节的Name、32 字节的Key(AES-256 加密密钥)和 16 字节的MAC(HMAC-SHA256 密钥) - 建议至少保留 2–3 个 key:最新 key 用于加密新 ticket,旧 key 用于解密已有 ticket,便于密钥轮换
- key 必须稳定持久化(如从文件加载),不能每次启动都随机生成,否则重启后所有 ticket 失效
cfg := &tls.Config{
MinVersion: tls.VersionTLS12,
// 必须显式设置,否则 ticket 被忽略
SessionTicketsKeys: []tls.SessionTicketKey{{
Name: [16]byte{ /* 自定义唯一标识 */ },
Key: [32]byte{ /* AES 密钥 */ },
MAC: [16]byte{ /* HMAC 密钥 */ },
}},
}客户端需启用 ClientSessionCache 并复用 tls.Config 实例
Go 的 http.Client 或自定义 TLS client 不会自动缓存 ticket —— 它依赖 tls.Config.ClientSessionCache 接口实现。若未设置,每次连接都是全新 handshake,0-RTT 和 session 复用全部失效。
- 标准库提供
tls.NewLRUClientSessionCache(64),适合大多数场景;容量设太小会导致频繁淘汰,太大则内存浪费 - 同一个
tls.Config实例必须复用(比如全局单例),否则 cache 无法跨连接生效 - 注意:HTTP/2 默认启用 ticket 复用,但 HTTP/1.1 下仍需手动配 cache 才有效
cache := tls.NewLRUClientSessionCache(128)
cfg := &tls.Config{
ServerName: "example.com",
MinVersion: tls.VersionTLS13,
ClientSessionCache: cache, // 关键:必须设
}TLS 1.3 下 SessionTicket 行为与 1.2 的关键差异
TLS 1.3 的 ticket 机制和语义已变更:它不再用于恢复完整会话状态,而是仅承载 PSK(Pre-Shared Key),用于 0-RTT 或 1-RTT 快速握手。这意味着:
立即学习“go语言免费学习笔记(深入)”;
- server 不再需要维护 session 状态,ticket 内容可完全无状态加密
- client 收到多个 ticket 时,会按有效期优先使用最近签发的;过期 ticket 自动丢弃
- 0-RTT 数据有重放风险,业务层必须做幂等防护,
tls.Config本身不处理 - 若要禁用 0-RTT(只允许 1-RTT 恢复),可设
cfg.RenewTicket = false,但 ticket 复用仍存在
真正容易被忽略的是密钥生命周期管理:生产环境必须定期轮换 SessionTicketsKeys,且旧 key 至少保留一个完整 ticket 生命周期(默认 7 天),否则正在传输中的 ticket 会突然失效,引发大量 full handshake 回退。这和证书轮换不同,没有 OCSP 或 CRL 机制,全靠运维同步控制。


















