应按公式“所需mb≈每秒新会话数×超时秒数×0.5kb”精准计算ssl_session_cache大小,如1200新会话/秒、4h超时需约8.2mb,起步配shared:ssl:10m;必须唯一声明于http块顶层,禁用builtin,配合ssl_session_tickets并监控$ssl_session_reused复用率。

高内存服务器不等于可以随意分配 SSL session 缓存。缓存过大反而引发锁竞争、内存碎片和初始化延迟;过小则导致 TLS 复用率暴跌、CPU 握手开销飙升。关键不是“有多少内存可用”,而是“每秒实际新建多少会话、它们平均活多久、每条占多少空间”。
按真实握手频率算容量,别拍脑袋填 10m
shared:SSL:10m 是常见默认值,但不是推荐值。实际所需大小应基于生产流量估算:
- 核心公式:所需 MB ≈ 每秒新会话数(HPS) × ssl_session_timeout(秒) × 0.5KB
- 例如:实测峰值 1200 次/秒新握手,
ssl_session_timeout 4h(14400 秒),理论需约 1200 × 14400 × 0.5 / 1024 ≈ 8437 KB ≈ 8.2 MB,起步配shared:SSL:10m合理 - 若启用 OCSP Stapling 或证书链很长(含多个中间 CA),单条开销可能达 2–3 KB,此时需按比例上调,如 1200 HPS × 2.5KB × 14400 / 1024 ≈ 42 MB,建议直接设
shared:SSL:48m并留冗余 - 注意:HPS ≠ QPS,要从日志中统计
$ssl_handshake_time非空出现频次,或用stub_status的 accepts 与 active 差值辅助估算
必须统一写在 http 块顶层,且只声明一次
Nginx 不支持 per-server 共享缓存。重复在多个 server 块里写 ssl_session_cache shared:SSL:10m 不仅无效,还可能触发未定义行为:
- 只有 http 块中**首次出现**的那条声明生效,其余同名配置被忽略(无报错、无提示)
- worker 进程共享的是主进程初始化的那一块内存区,server 级配置无法改变其生命周期或锁机制
- 验证方式:运行
nginx -T | grep "ssl_session_cache.*shared",输出应仅一行,且位置在http {之后、任何server {之前 - 错误写法示例:
http { ssl_session_cache shared:SSL:10m; server { ssl_session_cache shared:SSL:5m; } }→ 实际只用 10m,5m 被丢弃
别混用 builtin,它对复用率基本没帮助
builtin:1000 是每个 worker 私有缓存,看似简单,但在真实部署中几乎无效:
- 客户端重连由内核负载均衡(如 SO_REUSEPORT)或 accept_mutex 策略分发,极少固定到同一 worker
- 结果就是 A worker 缓存了会话,B worker 收到重连却查不到,只能走完整握手
- 除非你明确做了 stickysession(如绑定 CPU 核心 + 外部 LB 会话保持),否则 builtin 对提升复用率毫无意义
- 调试阶段可临时加
builtin:500辅助验证 handshake 流程,但上线必须依赖 shared
监控复用率比看内存使用率更关键
缓存是否够用,不能只看 free -m 显示用了多少——溢出不会报错,只会静默退化为全握手:
- 在
log_format中加入$ssl_session_reused,统计r(复用)与.(未复用)比例;长期低于 60% 就该扩容 - 观察
stub_status中Active connections与accepts的比值:若连接数远低于请求数,说明 keepalive + session 复用已在起效 - 若 CPU 使用率突增、TLS 握手时间变长、
$ssl_handshake_time日志中非空值密集出现,大概率是缓存不足导致频繁驱逐 - 注意 TLS 1.3 影响:它默认禁用 session ID 复用,主要靠 ticket;此时 shared 缓存压力下降,但
ssl_session_ticket_key轮换策略和密钥安全性变得更关键











