每次 TLS 完整握手需要 1-2 RTT 的额外延迟,加上非对称加密运算的 CPU 开销——在高并发场景下,TLS 的性能成本不可忽视。Cloudflare 公开的数据显示,TLS 1.2 完整握手的服务端 CPU 开销约为纯 HTTP 的 3.5 倍,其中密钥交换(ECDHE)占了约 70%。
但”TLS 太慢”是一个过时的说法。现代硬件和协议优化已经把 TLS 的性能开销压缩到了几乎可以忽略的程度——前提是你做了正确的配置。Session Ticket 可以将握手从 2 RTT 降到 1 RTT(TLS 1.2)甚至 0 RTT(TLS 1.3),OCSP Stapling 可以消除证书验证时的额外 DNS + HTTP 请求,AES-NI 指令集让对称加密的吞吐量提升了 5-10 倍。
本文从三个维度系统性地分析 TLS 性能优化的工程实践。
一、TLS 握手的性能开销分解
在优化之前,先搞清楚 TLS 握手到底慢在哪里。TLS 握手的开销可以分为两部分:网络延迟和计算开销。
1.1 网络延迟
TLS 1.2 完整握手需要 2 RTT:
RTT 1: ClientHello → ServerHello + Certificate + ServerKeyExchange + ServerHelloDone
RTT 2: ClientKeyExchange + ChangeCipherSpec + Finished → ChangeCipherSpec + Finished
TLS 1.3 将完整握手压缩到 1 RTT:
RTT 1: ClientHello + KeyShare → ServerHello + EncryptedExtensions + Certificate + Finished
(客户端收到后立即发送 Finished,开始传输应用数据)
加上 TCP 三次握手的 1 RTT,一个 HTTPS 连接在 TLS 1.2 下需要 3 RTT 才能开始传输数据,TLS 1.3 下需要 2 RTT。
对于跨洋连接(RTT ≈ 150 ms),TLS 1.2 的握手延迟就是 450 ms,仅握手就消耗了接近半秒。
1.2 计算开销
TLS 握手的计算开销主要来自三个部分:
| 操作 | 算法 | 典型耗时(现代 CPU) | 说明 |
|---|---|---|---|
| 密钥交换 | ECDHE P-256 | ~0.5 ms | 服务端和客户端各一次 |
| 签名验证 | RSA-2048 | ~0.1 ms | 验证服务端证书签名 |
| 签名生成 | RSA-2048 | ~1.0 ms | 服务端签名(最重的操作) |
| 签名生成 | ECDSA P-256 | ~0.2 ms | 比 RSA 签名快 5 倍 |
| 对称加密 | AES-128-GCM | ~1 GB/s (AES-NI) | 握手完成后的数据传输 |
可以看出,服务端 RSA 签名是握手中最重的计算操作。这也是为什么 ECDSA 证书在高并发场景下越来越受欢迎——签名速度快 5 倍意味着同样的 CPU 可以处理 5 倍的新连接。
1.3 用 openssl speed 量化本机性能
在做任何优化之前,先用 openssl speed
了解你的服务器的加密性能基线:
# 测试 RSA 签名/验证性能
openssl speed rsa2048
# 测试 ECDSA 签名/验证性能
openssl speed ecdsap256
# 测试 ECDHE 密钥交换性能
openssl speed ecdh
# 测试 AES-GCM 对称加密性能
openssl speed -evp aes-128-gcm
# 对比有无 AES-NI 的性能差异
openssl speed -evp aes-128-gcm # 使用 AES-NI(默认)
OPENSSL_ia32cap="~0x200000200000000" openssl speed -evp aes-128-gcm # 禁用 AES-NI一个典型的结果示例(Intel Xeon E5-2686 v4):
sign verify sign/s verify/s
rsa 2048 bits 0.000645s 0.000023s 1550 43478
sign verify sign/s verify/s
ecdsa P-256 0.000019s 0.000057s 52631 17544
RSA-2048 每秒签名 1550 次,ECDSA P-256 每秒签名 52631 次——ECDSA 签名快了 34 倍。但注意验证方向相反:RSA 验证比 ECDSA 验证快约 2.5 倍。对于服务端来说,签名次数远多于验证次数,所以 ECDSA 的优势更明显。
二、会话恢复:避免重复握手
会话恢复(Session Resumption)是最有效的 TLS 性能优化手段。它的核心思想是:既然完整握手的开销那么大,那对于已经建立过连接的客户端,能不能跳过大部分握手步骤?
2.1 TLS 1.2 的两种会话恢复机制
TLS 1.2 提供了两种会话恢复方式:Session ID 和 Session Ticket。
Session ID 是最早的方案。服务端在完整握手后为每个会话分配一个 32 字节的 ID,并把会话状态(主密钥、密码套件等)存储在服务端内存中。客户端下次连接时携带这个 ID,服务端查找到对应的状态就可以跳过完整握手。
Session ID 的工程问题:
- 服务端内存压力:每个会话占用约 256 字节。百万并发意味着约 256 MB 的会话缓存。
- 多服务器同步困难:在多台服务器(如 Nginx upstream)的场景下,客户端的第二次连接可能被调度到另一台服务器,那里没有它的 Session ID 记录。解决方案是共享会话缓存(如 memcached/Redis),但引入了额外的延迟和复杂性。
- 无法控制失效:服务端必须主动管理会话缓存的过期策略。
Session Ticket(RFC 5077)解决了这些问题。服务端把会话状态加密后打包成一个 Ticket,发给客户端保存。下次连接时客户端携带 Ticket,服务端解密后即可恢复会话——不需要在服务端存储任何状态。
完整握手后:
Server → Client: NewSessionTicket(加密的会话状态)
恢复握手:
Client → Server: ClientHello + SessionTicket
Server → Client: ServerHello + ChangeCipherSpec + Finished
Client → Server: ChangeCipherSpec + Finished
恢复握手只需要 1 RTT(比完整握手少 1 RTT),而且跳过了最重的密钥交换和证书签名操作。
2.2 Nginx 配置 Session Ticket
http {
# 启用 Session Ticket
ssl_session_tickets on;
# Session Ticket 密钥(48 字节,AES-256-CBC 加密)
# 必须手动生成并在所有服务器间共享
ssl_session_ticket_key /etc/nginx/ssl/ticket.key;
# 可以指定多个密钥实现轮换
# 第一个是当前密钥(用于加密新 Ticket),其余仅用于解密旧 Ticket
ssl_session_ticket_key /etc/nginx/ssl/ticket-current.key;
ssl_session_ticket_key /etc/nginx/ssl/ticket-previous.key;
# Session 缓存(作为 Session ID 的回退)
ssl_session_cache shared:SSL:50m; # 50 MB 共享缓存,约 20 万个会话
ssl_session_timeout 1h; # 会话有效期 1 小时
}
生成 Session Ticket 密钥:
# 生成 48 字节的随机密钥
openssl rand 48 > /etc/nginx/ssl/ticket.key
chmod 600 /etc/nginx/ssl/ticket.key
# 密钥轮换脚本(建议每 12 小时轮换一次)
#!/bin/bash
TICKET_DIR="/etc/nginx/ssl"
mv "$TICKET_DIR/ticket-current.key" "$TICKET_DIR/ticket-previous.key"
openssl rand 48 > "$TICKET_DIR/ticket-current.key"
chmod 600 "$TICKET_DIR/ticket-current.key"
nginx -s reload2.3 Session Ticket 的安全问题
Session Ticket 有一个严重的安全隐患:它破坏了前向保密性(Forward Secrecy)。
即使握手使用了 ECDHE 实现前向保密,Session Ticket 密钥是长期存在的对称密钥。如果攻击者获取了 Ticket 密钥,就可以解密所有使用该密钥加密的 Session Ticket,进而恢复所有对应会话的主密钥,解密录制的流量。
这意味着:
- Ticket 密钥必须定期轮换——建议不超过 24 小时,最好 12 小时。
- 旧 Ticket 密钥必须安全销毁——不能只是覆盖文件,还要确保内存中的副本被清除。
- Ticket 密钥不应存储在磁盘上(理想情况)——一些高安全性部署使用 HSM 或内存文件系统。
2.4 TLS 1.3 的 PSK 恢复与 0-RTT
TLS 1.3 重新设计了会话恢复机制,使用 PSK(Pre-Shared Key)模式。完整握手后,服务端发送 NewSessionTicket 消息,其中包含一个 PSK 身份标识。客户端在后续连接中使用这个 PSK 进行恢复握手。
TLS 1.3 PSK 恢复的改进:
- 始终保持前向保密:TLS 1.3 的恢复握手仍然执行 ECDHE 密钥交换(PSK + ECDHE 模式),所以即使 PSK 泄露,过去的会话仍然安全。
- Ticket 加密绑定到密钥调度:TLS 1.3 使用 HKDF 从恢复密钥派生所有会话密钥,加密学上更严格。
TLS 1.3 的 0-RTT 模式更进一步——允许客户端在握手完成之前就发送应用数据:
Client → Server: ClientHello + PSK + KeyShare + EarlyData(0-RTT 应用数据)
Server → Client: ServerHello + EncryptedExtensions + Finished
0-RTT 的延迟收益巨大(节省 1 RTT),但有重放攻击风险——详见TLS 1.3 工程实践。
Nginx 中启用 TLS 1.3 0-RTT:
server {
ssl_early_data on;
# 在后端请求中添加标记,让应用层感知 0-RTT
proxy_set_header Early-Data $ssl_early_data;
# 应用层对 0-RTT 请求应拒绝非幂等操作
# 例如:if (request.header("Early-Data") == "1") { reject POST/PUT/DELETE }
}
2.5 会话恢复的监控
验证会话恢复是否生效:
# 用 openssl s_client 测试 Session Ticket
# 第一次连接,保存 Session
openssl s_client -connect example.com:443 -sess_out /tmp/session.pem -brief
# 输出中应有: New, TLSv1.3, ...
# 第二次连接,使用保存的 Session
openssl s_client -connect example.com:443 -sess_in /tmp/session.pem -brief
# 输出中应有: Reused, TLSv1.3, ...
# 检查 Nginx 的 Session 命中率
# 通过 stub_status 或日志变量
# 在 log_format 中添加 $ssl_session_reused(值为 r 表示恢复)用 curl 测量握手耗时:
# 全新连接(完整握手)
curl -w "TCP: %{time_connect}s\nTLS: %{time_appconnect}s\nTotal: %{time_total}s\n" \
-o /dev/null -s https://example.com
# 对比有无 Session Ticket 的差异
# 完整握手: TLS 耗时约 60-80 ms(跨地域)
# 恢复握手: TLS 耗时约 30-40 ms(节省 1 RTT)三、OCSP Stapling:消除证书验证延迟
当客户端收到服务端的证书时,需要验证证书是否被吊销。传统方式是客户端向 CA 的 OCSP(Online Certificate Status Protocol)服务器发起查询——这需要一次额外的 DNS 解析加 HTTP 请求,延迟通常在 100-300 ms。
3.1 OCSP 的工程问题
传统 OCSP 查询有三个严重的工程问题:
延迟:客户端在 TLS 握手过程中发起 OCSP 查询,这个查询是阻塞的。如果 OCSP 服务器响应慢(常见于 Let’s Encrypt 等免费 CA),用户感受到的连接建立时间会显著增加。
隐私:客户端向 CA 的 OCSP 服务器查询特定证书的状态,CA 因此能知道用户在访问哪些网站。
可用性:如果 OCSP 服务器不可达(宕机、网络问题、被防火墙阻断),客户端必须在”拒绝连接(硬失败)“和”跳过验证(软失败)“之间选择。大多数浏览器选择软失败——这意味着证书吊销在实际中形同虚设。
3.2 OCSP Stapling 的工作原理
OCSP Stapling(RFC 6066 / RFC 6961)将验证工作从客户端转移到服务端。服务端定期向 CA 获取自己证书的 OCSP 响应(一个由 CA 签名的”证书未被吊销”证明),并在 TLS 握手时随证书一起发给客户端。
传统 OCSP:
Client → CA OCSP Server: 这个证书有效吗?(额外 DNS + HTTP 请求)
CA OCSP Server → Client: 有效(延迟 100-300 ms)
OCSP Stapling:
Server → CA OCSP Server: 给我一份 OCSP 响应(服务端后台定期获取)
Server → Client: 证书 + OCSP 响应(在 TLS 握手中,零额外延迟)
好处显而易见:
- 消除客户端的 OCSP 查询延迟:省去 100-300 ms 的证书验证时间。
- 保护用户隐私:CA 不知道谁在访问你的网站。
- 提高可用性:即使 OCSP 服务器临时宕机,服务端缓存的响应仍然有效(通常有效期 7 天)。
3.3 Nginx 配置 OCSP Stapling
server {
listen 443 ssl;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
# 启用 OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
# 指定信任链证书(用于验证 OCSP 响应)
# 通常是中间 CA 证书 + 根 CA 证书
ssl_trusted_certificate /etc/nginx/ssl/chain.pem;
# DNS 解析器(用于解析 OCSP 服务器地址)
# 建议使用本地缓存 DNS 或公共 DNS
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
}
验证 OCSP Stapling 是否生效:
# 方法 1:用 openssl s_client 检查
openssl s_client -connect example.com:443 -status </dev/null 2>&1 | \
grep -A 20 "OCSP Response"
# 应看到 "OCSP Response Status: successful"
# 方法 2:检查 OCSP 响应详情
echo | openssl s_client -connect example.com:443 -status 2>&1 | \
openssl ocsp -respin /dev/stdin -text -noverify
# 查看 Cert Status: good 和有效期
# 方法 3:用 curl 的 verbose 模式
curl -v https://example.com 2>&1 | grep -i "OCSP"3.4 OCSP Stapling 的常见问题
问题 1:Nginx 首次启动时没有 OCSP 响应
Nginx 在第一次收到客户端请求后才会去获取 OCSP 响应。这意味着启动后的第一个请求不会包含 OCSP Stapling。解决方案:
# 预获取 OCSP 响应
OCSP_URL=$(openssl x509 -in cert.pem -noout -ocsp_uri)
openssl ocsp -issuer chain.pem -cert cert.pem \
-url "$OCSP_URL" -respout /etc/nginx/ssl/ocsp.resp -noverify
# 在 Nginx 配置中指定预获取的响应
ssl_stapling_file /etc/nginx/ssl/ocsp.resp;配合 cron 定期更新:
# /etc/cron.d/ocsp-update
0 */6 * * * root /usr/local/bin/update-ocsp.sh && nginx -s reload问题 2:OCSP 服务器不可达
如果 Nginx 无法联系 OCSP 服务器,它会静默地不提供 Stapling。排查方法:
# 检查 Nginx 错误日志
grep -i "ocsp" /var/log/nginx/error.log
# 手动测试 OCSP 服务器连通性
OCSP_URL=$(openssl x509 -in cert.pem -noout -ocsp_uri)
curl -v "$OCSP_URL"
# 检查 DNS 解析
dig $(echo "$OCSP_URL" | sed 's|http://||;s|/.*||')问题 3:多级证书链的 OCSP Stapling
如果你的证书链有多个中间 CA,需要确保
ssl_trusted_certificate 包含完整的信任链:
# 构建完整信任链
cat intermediate.pem root.pem > chain.pem3.5 OCSP Must-Staple
OCSP Must-Staple(RFC 7633)更进一步:在证书中嵌入一个扩展,告诉客户端”这个证书必须带有 OCSP Stapling 响应,否则拒绝连接”。
这解决了 OCSP Stapling 的一个局限:普通 OCSP Stapling 是可选的——如果服务端不提供 Stapling,客户端会回退到自行查询或直接跳过。攻击者可以利用这一点,在中间人攻击中剥离 Stapling 响应,迫使客户端软失败从而跳过吊销检查。
Must-Staple 的使用注意事项:
- 运维风险极高:如果 OCSP 响应获取失败且缓存过期,所有客户端都会拒绝连接——相当于自己把网站”吊销”了。
- 必须配合完善的 OCSP 响应更新机制:多层缓存、定时预获取、监控告警。
- 建议只在高安全性场景使用:金融、医疗等对证书吊销有强需求的环境。
生成 Must-Staple 证书的 CSR:
# 在 CSR 中添加 Must-Staple 扩展
openssl req -new -key server.key -out server.csr \
-addext "tlsfeature = status_request"
# 或者使用 certbot
certbot certonly --must-staple -d example.com四、AES-NI 硬件加速
TLS 握手完成后,所有的应用数据都使用对称加密(通常是 AES-GCM)保护。现代 CPU 几乎都支持 AES-NI(Advanced Encryption Standard New Instructions)指令集,可以在硬件层面加速 AES 运算。
4.1 检查 AES-NI 支持
# 检查 CPU 是否支持 AES-NI
grep -o aes /proc/cpuinfo | head -1
# 输出 "aes" 表示支持
# 更详细的检查
cat /proc/cpuinfo | grep flags | head -1 | tr ' ' '\n' | grep -E "aes|avx|sse"
# 检查 OpenSSL 是否使用了 AES-NI
openssl speed -evp aes-128-gcm 2>&1 | head -5
# 如果使用了 AES-NI,吞吐量通常 > 3 GB/s4.2 AES-NI 的性能影响
用 openssl speed 对比有无 AES-NI
的性能差异:
# 启用 AES-NI(默认)
openssl speed -evp aes-128-gcm
# 典型结果: ~5 GB/s
# 禁用 AES-NI
OPENSSL_ia32cap="~0x200000200000000" openssl speed -evp aes-128-gcm
# 典型结果: ~0.5 GB/s性能对比(Intel Xeon Platinum 8275CL):
| 算法 | 无 AES-NI | 有 AES-NI | 加速比 |
|---|---|---|---|
| AES-128-GCM | 0.5 GB/s | 5.0 GB/s | 10x |
| AES-256-GCM | 0.4 GB/s | 4.2 GB/s | 10.5x |
| ChaCha20-Poly1305 | 1.8 GB/s | 1.8 GB/s | 1x(不使用 AES-NI) |
注意 ChaCha20-Poly1305 不使用 AES-NI——它使用通用的 SIMD 指令(SSE/AVX)。这意味着在不支持 AES-NI 的平台(如早期 ARM 处理器)上,ChaCha20 可能比 AES 更快。这就是为什么 Google 在 Android 上优先使用 ChaCha20-Poly1305。
4.3 密码套件选择与硬件加速
密码套件的选择应该考虑硬件加速能力:
# 推荐的密码套件配置(优先 AES-GCM,利用 AES-NI)
ssl_ciphers 'TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
ssl_prefer_server_ciphers on;
但如果你的客户端大量使用移动设备(特别是旧版 Android),考虑优先提供 ChaCha20:
# 移动优先的配置:让客户端选择(移动设备会优先选 ChaCha20)
ssl_prefer_server_ciphers off;
4.4 Intel QAT 与 SSL 加速卡
对于超高并发场景(每秒数万新连接),可以考虑使用硬件 SSL 加速卡,如 Intel QuickAssist Technology(QAT)。
QAT 的工作原理是将非对称加密运算(RSA/ECDSA 签名、ECDHE 密钥交换)卸载到专用硬件上,释放 CPU 资源给应用逻辑。
# 检查 QAT 设备
lspci | grep -i quickassist
# QAT 引擎配置(OpenSSL 引擎模式)
# 需要安装 QAT 驱动和 OpenSSL 引擎
openssl engine -t qatengine
# (qatengine) Reference implementation of QAT crypto engine
# [ available ]Nginx 使用 QAT 引擎:
# 需要编译 Nginx with --with-openssl 并配置 QAT 引擎
ssl_engine qatengine;
# 或在 OpenSSL 配置文件中全局启用
# /etc/ssl/openssl.cnf
[engine_section]
qatengine = qatengine_section
[qatengine_section]
engine_id = qatengine
default_algorithms = RSA,EC,DH
QAT 的工程考量:
- 收益场景:每秒 > 10000 新 TLS 连接时,QAT 能显著降低 CPU 使用率(释放 30-50% CPU)。
- 不适用场景:如果大部分连接使用会话恢复(跳过非对称加密),QAT 的收益有限。
- 延迟影响:QAT 的异步模式可能增加单个请求的延迟(~0.1 ms),但提高整体吞吐量。
- 运维复杂性:需要安装驱动、配置引擎、处理固件更新——增加了运维负担。
五、证书算法对性能的影响
证书使用的签名算法对 TLS 握手性能有直接影响。
5.1 RSA vs ECDSA 性能对比
# RSA-2048 签名性能
openssl speed rsa2048
# sign: ~1550/s verify: ~43000/s
# RSA-4096 签名性能
openssl speed rsa4096
# sign: ~280/s verify: ~16000/s
# ECDSA P-256 签名性能
openssl speed ecdsap256
# sign: ~52000/s verify: ~17500/s
# ECDSA P-384 签名性能
openssl speed ecdsap384
# sign: ~7200/s verify: ~3400/s| 算法 | 签名/秒 | 验证/秒 | 证书大小 | 安全强度 |
|---|---|---|---|---|
| RSA-2048 | ~1,550 | ~43,000 | ~1,200 B | 112 bit |
| RSA-4096 | ~280 | ~16,000 | ~2,400 B | 128 bit |
| ECDSA P-256 | ~52,000 | ~17,500 | ~300 B | 128 bit |
| ECDSA P-384 | ~7,200 | ~3,400 | ~400 B | 192 bit |
ECDSA P-256 在服务端签名速度上比 RSA-2048 快 33 倍,且证书体积只有 1/4。对于高并发场景,ECDSA 是明确的赢家。
5.2 双证书部署
为了兼容不支持 ECDSA 的老客户端,Nginx 支持同时配置 RSA 和 ECDSA 两套证书:
server {
listen 443 ssl;
# RSA 证书(兼容老客户端)
ssl_certificate /etc/nginx/ssl/rsa-cert.pem;
ssl_certificate_key /etc/nginx/ssl/rsa-key.pem;
# ECDSA 证书(现代客户端优先使用)
ssl_certificate /etc/nginx/ssl/ecdsa-cert.pem;
ssl_certificate_key /etc/nginx/ssl/ecdsa-key.pem;
}
Nginx 会根据客户端的 ClientHello 中支持的签名算法,自动选择合适的证书。现代客户端会优先使用 ECDSA。
5.3 证书链长度优化
证书链中每增加一个中间证书,TLS 握手的数据传输量就增加约 1-2 KB。在移动网络(高延迟、高丢包)上,这个增量对握手时间的影响不可忽略。
优化建议:
# 检查证书链长度
echo | openssl s_client -connect example.com:443 2>/dev/null | \
grep -E "Certificate chain|s:|i:"
# 理想的证书链:
# 0 s:CN = example.com (你的证书)
# 1 i:CN = Let's Encrypt R3 (中间 CA)
# 2 i:CN = ISRG Root X1 (根 CA)
# 不要发送根证书!客户端已经内置了根证书
# 只发送你的证书 + 必要的中间 CA 证书
cat server.crt intermediate.crt > fullchain.pem
# 不要加入 root.crt六、TLS 性能基准测试方法论
优化后如何验证效果?需要一套系统化的基准测试方法。
6.1 使用 h2load 测试 TLS 握手吞吐量
# 安装 h2load(来自 nghttp2)
apt install nghttp2-client
# 测试新连接吞吐量(每个请求都建立新的 TLS 连接)
h2load -n 10000 -c 100 -t 4 --h1 https://example.com/
# -n: 总请求数
# -c: 并发连接数
# -t: 线程数
# --h1: 使用 HTTP/1.1(避免 HTTP/2 多路复用干扰测试)
# 关键指标:
# - req/s: 每秒请求数(包含 TLS 握手)
# - time for connect: 平均 TCP + TLS 建连时间
# - time for 1st byte: TTFB6.2 使用 openssl s_time 测试纯 TLS 握手性能
# 测试 30 秒内能完成多少次完整 TLS 握手
openssl s_time -connect example.com:443 -new -time 30
# 输出: N connections in M seconds => X connections/s
# 测试会话恢复的 TLS 握手
openssl s_time -connect example.com:443 -reuse -time 30
# 恢复握手的速率通常是完整握手的 3-5 倍6.3 使用 wrk 测试端到端性能
# 安装 wrk
apt install wrk
# 基线测试:HTTPS 端到端性能
wrk -t 4 -c 100 -d 30s https://example.com/api/health
# -t: 线程数
# -c: 并发连接数
# -d: 测试持续时间
# wrk 默认复用连接(Keep-Alive),所以测的是对称加密的吞吐量
# 如果要测试握手性能,需要强制每次建立新连接6.4 关键性能指标
建立 TLS 性能基线时应关注的指标:
| 指标 | 说明 | 健康阈值 |
|---|---|---|
| 新连接速率 | 每秒完整 TLS 握手数 | > 5000/s(单核 ECDSA) |
| 会话恢复率 | 恢复握手占总握手的比例 | > 80% |
| 握手延迟 P50 | 完整握手的中位延迟 | < 20 ms(同地域) |
| 握手延迟 P99 | 完整握手的 99 分位延迟 | < 50 ms(同地域) |
| OCSP Stapling 命中率 | 携带 OCSP 响应的握手比例 | > 99% |
| 对称加密吞吐量 | AES-GCM 加密的数据吞吐 | > 3 GB/s(有 AES-NI) |
6.5 Prometheus 监控 TLS 性能
用 Nginx 的 ssl_session_reused 变量和
Prometheus exporter 采集 TLS 指标:
# 在 log_format 中记录 TLS 相关信息
log_format tls_metrics '$remote_addr $ssl_protocol $ssl_cipher '
'$ssl_session_reused $ssl_early_data '
'$request_time $upstream_response_time';
# $ssl_session_reused: "r" 表示恢复,"." 表示完整握手
# $ssl_early_data: "1" 表示 0-RTT 数据
配合 mtail 或 grok_exporter 将日志转为 Prometheus 指标:
# mtail 规则示例
counter tls_handshakes_total by type
/^.* (?P<reused>[r.]) / {
$reused == "r" {
tls_handshakes_total["resumed"]++
}
$reused == "." {
tls_handshakes_total["full"]++
}
}
七、TLS 1.2 vs TLS 1.3 性能对比
TLS 1.3 在性能上的改进不仅仅是”少了 1 RTT”。
7.1 握手延迟对比
TLS 1.2 完整握手: TCP(1 RTT) + TLS(2 RTT) = 3 RTT
TLS 1.2 恢复握手: TCP(1 RTT) + TLS(1 RTT) = 2 RTT
TLS 1.3 完整握手: TCP(1 RTT) + TLS(1 RTT) = 2 RTT
TLS 1.3 恢复握手: TCP(1 RTT) + TLS(1 RTT) = 2 RTT(但可以发 0-RTT 数据)
TLS 1.3 0-RTT: TCP(1 RTT) + TLS(0 RTT) = 1 RTT(应用数据随 ClientHello 发送)
在 RTT = 50 ms 的典型场景下:
| 场景 | TLS 1.2 | TLS 1.3 | 节省 |
|---|---|---|---|
| 完整握手 | 150 ms | 100 ms | 50 ms (33%) |
| 恢复握手 | 100 ms | 100 ms* | 0 ms |
| 0-RTT | 不支持 | 50 ms | 50 ms |
*TLS 1.3 恢复握手的 RTT 数与 TLS 1.2 恢复相同,但 TLS 1.3 可以在第一个 RTT 就发送应用数据(0-RTT),这是 TLS 1.2 做不到的。
在高延迟场景(如跨洋连接、卫星网络)中,这些差异被成倍放大。当 RTT 从 50 ms 增加到 200 ms 时,TLS 1.3 完整握手(200 ms)比 TLS 1.2 完整握手(400 ms)节省了整整 200 ms。
7.2 计算开销对比
TLS 1.3 移除了 RSA 密钥交换,只保留了 ECDHE。这意味着:
- 完整握手:TLS 1.3 略快(因为消除了 RSA 密钥交换路径的可能性,ECDHE 计算量更少)。
- 恢复握手:TLS 1.3 更安全但略重(PSK + ECDHE,保持前向保密)。TLS 1.2 的 Session Ticket 恢复不需要 ECDHE。
- 握手消息加密:TLS 1.3 在 ServerHello 之后就加密了所有握手消息,增加了一点计算开销,但提高了安全性。
7.3 升级建议
# 推荐配置:优先 TLS 1.3,兼容 TLS 1.2
ssl_protocols TLSv1.2 TLSv1.3;
# TLS 1.3 密码套件(在 OpenSSL 1.1.1+ 中自动配置)
# 不需要手动指定 TLS 1.3 密码套件
# TLS 1.2 密码套件(仅保留安全的 ECDHE + AEAD 组合)
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305';
ssl_prefer_server_ciphers off; # 让客户端选择最适合的密码套件
八、完整的 Nginx TLS 性能优化配置
把前面所有优化整合到一个配置模板中:
# /etc/nginx/conf.d/tls-optimized.conf
# 全局 TLS 配置
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305';
ssl_prefer_server_ciphers off;
# ECDHE 参数
ssl_ecdh_curve X25519:P-256:P-384;
# Session Ticket(会话恢复)
ssl_session_tickets on;
ssl_session_ticket_key /etc/nginx/ssl/ticket-current.key;
ssl_session_ticket_key /etc/nginx/ssl/ticket-previous.key;
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1h;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/ssl/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# TLS 1.3 0-RTT(仅在应用层支持幂等检查时启用)
# ssl_early_data on;
# 缓冲区优化
ssl_buffer_size 4k; # 小页面用 4k(减少 TTFB),大文件传输可设为 16k
server {
listen 443 ssl http2;
server_name example.com;
# 双证书(ECDSA 优先,RSA 兼容)
ssl_certificate /etc/nginx/ssl/ecdsa-cert.pem;
ssl_certificate_key /etc/nginx/ssl/ecdsa-key.pem;
ssl_certificate /etc/nginx/ssl/rsa-cert.pem;
ssl_certificate_key /etc/nginx/ssl/rsa-key.pem;
# HSTS(强制 HTTPS)
add_header Strict-Transport-Security "max-age=63072000" always;
}
8.1 ssl_buffer_size 的工程权衡
ssl_buffer_size
是一个经常被忽略但影响显著的参数。它控制了 TLS
记录层每次加密的数据块大小。
- 默认值 16 KB:适合大文件传输(减少加密次数),但第一个 TLS 记录必须等待 16 KB 数据才会发送。
- 设为 4 KB:适合小页面和 API 响应(减少 TTFB),代价是加密操作更频繁。
# API 服务(小响应体,追求低 TTFB)
ssl_buffer_size 4k;
# 文件下载服务(大文件,追求高吞吐)
ssl_buffer_size 16k;
Cloudflare 的实践是使用动态的 TLS
记录大小:先发送小记录(1369
字节,刚好不分片),在连接”预热”后逐渐增大到 16 KB。Nginx
不原生支持这个行为,但 OpenResty 的
ssl_dyn_rec_size_lo/ssl_dyn_rec_size_hi
补丁可以实现类似效果。
九、性能优化的优先级矩阵
不是所有优化都需要做。根据你的场景选择优先级:
| 优化措施 | 延迟收益 | 吞吐收益 | 实施难度 | 推荐优先级 |
|---|---|---|---|---|
| 升级 TLS 1.3 | ★★★ | ★★ | ★ | P0 |
| 启用 Session Ticket | ★★★ | ★★★ | ★ | P0 |
| 启用 OCSP Stapling | ★★ | ★ | ★ | P0 |
| 使用 ECDSA 证书 | ★ | ★★★ | ★★ | P1 |
| 双证书部署 | ★ | ★★ | ★★ | P1 |
| 优化证书链 | ★ | ★ | ★ | P1 |
| 调整 ssl_buffer_size | ★★ | ★ | ★ | P2 |
| 启用 0-RTT | ★★★ | ★ | ★★★ | P2 |
| 部署 QAT 加速卡 | ★ | ★★★ | ★★★ | P3 |
| Must-Staple | ★ | ★ | ★★★ | P3 |
P0 是每个 HTTPS 服务都应该做的,只需要几行配置。P1 在高并发场景下收益明显。P2 和 P3 只在特定场景下值得投入。
十、总结
TLS 性能优化的核心思路只有三个:减少握手次数(会话恢复)、减少握手延迟(TLS 1.3、OCSP Stapling)、降低计算开销(ECDSA、AES-NI、QAT)。
几个值得记住的数字:
- TLS 1.3 完整握手比 TLS 1.2 少 1 RTT(节省 33% 握手延迟)。
- Session Ticket 恢复可以将握手的 CPU 开销降低 90%(跳过非对称加密)。
- ECDSA P-256 的签名速度是 RSA-2048 的 33 倍。
- AES-NI 将对称加密吞吐量提升 10 倍。
- OCSP Stapling 消除 100-300 ms 的证书验证延迟。
- 0-RTT 可以节省 1 RTT,但有重放攻击风险,需要应用层配合。
最后一个建议:先测量,再优化。在没有基准数据的情况下做优化,你不知道是否真的改善了性能,还是只是”感觉”快了。openssl speed、h2load、openssl s_time——这三个工具足以建立可靠的
TLS 性能基线。
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
网络工程索引
汇总本站网络工程系列文章,覆盖分层模型、以太网、IP、TCP、DNS、TLS、HTTP/2/3、CDN、BGP 与故障诊断。
【网络工程】零拷贝网络:sendfile、splice 与 MSG_ZEROCOPY
数据从磁盘到网卡的传统路径涉及 4 次拷贝和多次上下文切换。本文系统剖析 sendfile、splice、vmsplice、MSG_ZEROCOPY 四种零拷贝技术的内核实现、适用场景与性能差异,并以 Kafka 和 Nginx 为案例分析零拷贝在生产系统中的工程实践。
【网络工程】CDN 与 HTTPS:边缘 TLS、证书管理与安全
CDN 的 HTTPS 部署涉及边缘 TLS 终止、证书托管、回源加密等多个工程环节。本文系统拆解 CDN HTTPS 的架构模式、证书管理方案、安全最佳实践与常见故障排查方法。
【网络工程】反向代理模式:TLS 终止、透传与重加密
系统解剖反向代理的三种 TLS 处理模式——终止、透传与重加密。从架构对比到 SNI 路由、证书管理、性能影响与安全权衡,给出生产环境的工程选型依据。