关键是按“每秒新建tls会话数×ssl_session_timeout(秒)×单条开销(kb)”精准计算shared:ssl缓存大小,须设于http块顶层、禁用builtin、启用session_tickets,并通过$ssl_session_reused等指标验证。

调优 Nginx 的 ssl_session_cache 不是填个“10m”就完事,关键在于让缓存大小匹配真实握手流量——太小,会话刚存进去就被挤掉,复用率暴跌;太大,反而引发锁竞争、内存碎片,甚至启动失败。
按真实握手频率算容量,别拍脑袋填
真正需要的共享缓存大小(单位 MB)≈ 每秒新建 TLS 会话数 × ssl_session_timeout(秒)× 单条开销(KB)。其中:
- 每秒新建会话数 ≠ QPS:要从日志中统计
$ssl_handshake_time非空出现频次,或用stub_status中accepts - active辅助估算;压测时可用openssl s_client -reconnect观察全握手频率 -
ssl_session_timeout建议设为 600–1200 秒(10–20 分钟):比默认 5 分钟更贴合浏览器 Keep-Alive 行为,复用率明显提升;设为 4 小时虽理论容量大,但易造成缓存长期驻留、碎片升高,且不满足 PCI DSS 等合规要求 - 单条开销不是固定值:普通证书链约 256–512 字节;若启用 OCSP Stapling 或含多个中间 CA,可能达 2–3 KB,此时需按比例上调——例如 800 新会话/秒 × 2.5KB × 1200 秒 ÷ 1024 ≈ 234 MB,可设
shared:SSL:256m
必须统一写在 http 块顶层,且只声明一次
Nginx 不支持 per-server 共享缓存,重复声明不仅无效,还可能触发未定义行为:
- 只有
http {块中首次出现的ssl_session_cache shared:SSL:XXm生效,其余同名配置被静默忽略(无报错、无提示) - worker 进程共享的是主进程初始化的那一块内存区,server 级配置无法改变其生命周期或锁机制
- 验证方式:运行
nginx -T | grep "ssl_session_cache.*shared",输出应仅一行,且位置在http {之后、任何server {之前
禁用 builtin,优先用 shared + session tickets
builtin:1000 是每个 worker 私有缓存,在真实部署中基本无效:
- 客户端重连由内核(如 SO_REUSEPORT)或 accept_mutex 策略分发,极少固定到同一 worker
- A worker 缓存了会话,B worker 收到重连却查不到,只能走完整握手
- 除非你明确做了 stickysession(如绑定 CPU 核心 + 外部 LB 会话保持),否则 builtin 对复用率提升几乎为零
- 推荐组合:
ssl_session_cache shared:SSL:10m;+ssl_session_tickets on;+ssl_session_ticket_key /path/to/ticket.key;
用三类指标验证是否真正生效
不能只看配置加载成功,要观察实际行为:
- 在
log_format中加入$ssl_session_reused变量,统计r(复用)与.(未复用)比例;复用率长期低于 60%,大概率是缓存太小被频繁淘汰 - 用
openssl s_client -connect example.com:443 -reconnect连接两次,观察第二次输出中是否含Reused, SSL handshake succeeded - 对比调优前后服务器 CPU 使用率,尤其在相同流量下握手相关计算是否明显下降











