“SSL 证书”这四个字,很多人的认知就停在浏览器地址栏那把锁上,真到自己要把服务推上线,才发现事情没那么简单:证书怎么申请、私钥放哪、链要不要拼、配完为什么安卓能开苹果打不开、到期忘了续怎么办。这篇就把 SSL 证书从原理到落地一次讲透,重点不在概念复述,而在那些文档里不写、但线上一定会撞上的细节。适合谁看:要亲手把 HTTP 切成 HTTPS 的后端和运维、自己维护站点的独立开发者、做内网服务需要发证的工程同学。哪怕你只配过一次自签证书,跟着往下走也能把整条链路补齐。我先给结论:SSL 证书的本质是“把公钥和域名绑定起来,并由一个双方都信的第三方盖章”,剩下的所有配置、报错、优化,都是围绕这句话展开的。
1. 先把底层逻辑捋直:SSL 证书解决的到底是什么问题
1.1 锁图标背后是三件事,不是一件
大部分人把证书等同于“加密”,其实它同时干三件事,缺一件整套体系就塌。第一件是身份确认:你连的到底是不是你以为的那个服务器,证书里的域名列表和当前访问的域名必须对得上。第二件是密钥协商:双方在不安全的信道里,商量出一个只有彼此知道的会话密钥。第三件是完整性保护:传输过程中任何一个字节被改动,接收端都能发现。
这三件事里最容易出事的其实是第一件。加密算法本身早就被验证得很充分,真正让线上翻车的,往往是证书过期、域名没覆盖到、中间证书没下发、系统时间不对这四类“非算法问题”。我见过一个挺典型的场景:某团队把服务从单机扩到多机,新机器的系统镜像是几年前的老版本,时间同步服务没起来,机器时间慢了十几个小时,结果证书校验直接判为“尚未生效”,接口全挂。排查了半天加密套件,最后发现是时间问题。
所以理解 SSL 证书,别一上来就钻密码学,先把这三个职责分清楚,后面遇到任何报错,你都能快速判断是哪一层出的问题。
1.2 一次握手到底发生了什么
我把 TLS 握手拆成四步来讲,用寄快递打个比方:你要给一个陌生仓库寄贵重物品,得先确认仓库身份、再商量一把只有你俩有的锁。
第一步,客户端发“你好”,带上自己支持的协议版本、加密套件清单、一个随机数。第二步,服务端回“你好”,选定协议版本和套件,也带一个随机数,紧接着把自己的证书链发过去。这里注意,服务端发的不是一张证书,而是一串,从自己这张(叶子证书)到中间证书,通常不含根证书。第三步,客户端验证这串证书,验证通过后生成第三个随机材料,用证书里的公钥加密发回去——在 TLS 1.2 里是这样,到了 TLS 1.3,这一步改成了基于椭圆曲线的密钥交换,双方各自算出一个共享秘密,私钥根本不参与加密传输,这也是 TLS 1.3 具备前向保密的原因。第四步,双方用前面这几个材料推导出会话密钥,之后所有业务数据都用这把对称密钥加密。
为什么要“非对称换对称”?因为非对称加密(RSA、ECC)算起来太贵,用它加密业务数据会让吞吐量掉一个数量级;而对称加密(AES、ChaCha20)快得多,但双方得先有同一把钥匙。所以标准做法是:用非对称的方式安全地把对称密钥送到对方手里,然后全程用对称加密跑数据。这是个非常经典的分工,理解了这个,你就能明白为什么证书里存的是公钥而不是什么“密码”。
1.3 证书链与信任锚:为什么你的证书“浏览器不认”
证书链是一棵树,根在最上面。操作系统和浏览器内置了一份受信任的根证书列表,这叫信任锚。服务端下发的证书需要能被这条链一路验证到某个根上,验证过程逐级校验签名,任何一环断了,或者中间证书缺失,客户端就会报“证书链不完整”。
我踩过最经典的一个坑:把服务端证书单独配上去,浏览器打开正常,但某些安卓机和部分命令行工具报错。原因就是域名验证用的中间证书没一起下发,桌面浏览器有时会自己去下载缺失的中间证书,看着像没问题,实际上是浏览器帮你兜了底;而很多移动端环境不会做这个补救动作,直接失败。
注意:服务端配置的证书文件,必须包含“叶子证书 + 全部中间证书”,顺序是叶子在前、中间在后,根证书不要放进去。多放根证书不算错但会白白增加握手字节数。
下面这张表是我平时用来快速定位“是哪一层的问题”的对照:
| 现象 | 大概率根因 | 位置 |
|---|---|---|
| 桌面正常、移动端报错 | 中间证书未下发 | 证书链 |
| 全部客户端报域名不匹配 | SAN 未覆盖该域名 | 证书内容 |
| 突然大面积报错、时间相关 | 证书过期或系统时间异常 | 有效期 |
| 只有部分老设备连不上 | 协议版本或套件过新 | 服务端配置 |
| 浏览器提示“不安全”但能访问 | 页面内有 HTTP 资源 | 页面内容 |
这张表建议存下来,八成线上问题三分钟内能对上号。
2. 选型决策:验证等级、域名覆盖与有效期
2.1 DV、OV、EV 的真实差异
证书按验证深度分三档。**DV(域名验证)**只验证你对这个域名有控制权,通常几分钟到几小时签发,免费或极低成本的证书基本都是这一档。**OV(组织验证)**在域名之外还要核验申请主体的真实存在,签发要几个工作日。**EV(扩展验证)**验证最严,早年浏览器会给个绿色地址栏显示主体名称,这个 UI 现在主流浏览器基本都取消了。
说实话,对 90% 的业务场景,DV 已经完全够用。加密强度和验证等级没有任何关系,DV 证书一样是 2048 位 RSA 或 P-256 椭圆曲线,握手过程一模一样。OV 和 EV 的价值主要体现在合规审计场景和对外展示的商务信任感上,比如某些行业采购方会明确要求提交 OV 及以上证书的证明。
我的建议很直接:内部系统、个人项目、API 网关,一律 DV 加自动化;面向企业客户的门户站,如果合同或审计有要求再上 OV。别为了那个已经消失的绿条多花预算。
2.2 单域名、多域名、泛域名:覆盖范围怎么算
按覆盖范围分,有单域名、多域名(SAN 证书,一张证书里塞多个域名)、泛域名(通配符)三种。这里有个极容易被误解的点:泛域名只覆盖一级子域。*.example.com能覆盖a.example.com和b.example.com,但覆盖不了a.b.example.com,也覆盖不了裸域example.com本身。很多团队买完泛域名发现主站还是报错,就是漏了裸域。
多域名证书适合域名集合固定、数量不多的场景,运维上最省事,一个文件搞定所有。泛域名适合子域动态增加的业务,比如每个客户一个二级域名的 SaaS 形态,这种场景下用多域名证书会疯掉。
还有一个隐性成本要算:泛域名通常只能走 DNS 验证,因为签发方无法预知你会用哪个子域,只能让你在 DNS 里放一条记录来证明控制权。这意味着你的签发流程必须能操作用户的 DNS 解析,或者至少能调 DNS 服务商的接口做自动更新。如果 DNS 托管在无法 API 操作的地方,泛域名的自动续期就会很别扭,这一点在选型阶段就要想清楚。
2.3 有效期越来越短,自动化不是可选项
行业趋势很明确:证书最长有效期一直在压缩,从几年到 398 天,未来还会更短。这背后的逻辑是,短周期证书能显著降低私钥泄露后的风险窗口,也让吊销机制的压力变小。
对运维来说,结论只有一条:证书续期必须全自动。手工续期的团队,早晚会在某个周五晚上收到告警。我见过最惨的一次是证书在长假第一天到期,值班同学不知道私钥在哪台机器上,翻了两小时才找到备份。
自动化的核心是 ACME 协议,它把“证明域名控制权—提交 CSR—拿回证书”这个流程标准化成了可编程的接口。执行流程并不复杂:
- 客户端在本地生成或复用一个密钥对。
- 向签发方申请,声明要签的域名。
- 根据签发方给出的挑战,完成 HTTP-01(在网站根目录放指定文件)或 DNS-01(在 DNS 里加一条 TXT 记录)验证。
- 验证通过后提交 CSR,取回证书。
- 落盘,然后重载服务进程。
关键在第五步之后:续期成功不等于生效,一定要在续期钩子里加一步服务重载,并且验证重载后的实际证书指纹是不是新的。我习惯在续期脚本最后加一句读取线上证书到期时间的检查,把结果打进日志,这样出问题能立刻发现。
3. 从生成私钥到拿到证书:完整实操链路
3.1 私钥与 CSR:参数怎么定
私钥是整个体系里最不能丢的东西。现代实践下有两个选择:RSA 2048/3072,或者 ECDSA P-256。
RSA 2048 的兼容性最好,老设备、老客户端、各类嵌入式环境通吃,但握手时密钥交换和签名计算更重,握手字节数也更多。ECDSA P-256 的安全强度大致相当于 RSA 3072,密钥和签名体积小一大截,握手更快,移动端弱网下感知明显。代价是极老的客户端可能不支持。
我的实际做法是双证书:给 RSA 和 ECDSA 各配一张,让服务端按客户端能力自动选择。这样兼容性和性能都不牺牲,配置上只多一行指令。
生成密钥和 CSR 的命令如下,几个参数解释一下:-nodes表示私钥不加密(服务进程启动时不需要交互输密码,线上必须这样);-subj里的字段如果不填,签发方一般会以域名验证结果为准,OV/EV 才需要完整主体信息。
# 生成 ECDSA 私钥 + CSR openssl req -new -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \ -nodes -keyout example.key -out example.csr \ -subj "/CN=www.example.com" # 生成 RSA 私钥 + CSR openssl req -new -newkey rsa:2048 -sha256 -nodes \ -keyout example-rsa.key -out example-rsa.csr \ -subj "/CN=www.example.com" # 检查 CSR 里到底写了什么 openssl req -in example.csr -noout -text私钥文件权限必须立刻收到 600,属主设为服务运行用户。这一步很多人会漏,私钥被别的进程读到就毫无意义了。另外,CSR 里的 CN 现在只作为兜底,真正决定域名覆盖的是 SAN 扩展,签发方一般会自动把所有申请的域名写进 SAN,所以别指望只改 CN 就能扩大覆盖。
3.2 域名验证的三种通道与各自坑点
HTTP-01:签发方要求你在http://域名/.well-known/acme-challenge/下放一个特定文件,它从公网访问能读到就算通过。坑点集中在两处:一是站点强制跳转 HTTPS,验证请求被 301 走,签发方跟不过去;二是前置的转发层、CDN 缓存把这个路径拦了或者缓存了旧响应。做 HTTP-01 前,务必把.well-known路径加进跳转白名单和缓存排除规则。
DNS-01:在 DNS 里加一条_acme-challenge的 TXT 记录。它是唯一支持泛域名的通道,也是唯一可以完全不暴露 HTTP 服务的通道。坑点在生效时间:有些解析服务商生效慢,客户端等不到记录生效就放弃了。碰上这种情况,把客户端的验证等待时间调长,或者干脆先手动确认 TXT 记录能查到再触发签发。
TLS-ALPN-01:在 443 端口上用特殊的 ALPN 标识回应验证,适合只能用 443 的严格环境。支持它的签发方相对少,一般用不上。
还有一个隐蔽的坑:如果域名解析还没切过来,或者源站有多个节点,验证请求随机打到没有放文件的那台机器上,就会失败。多节点场景下,要么保证文件在所有节点都有,要么统一走 DNS-01。
3.3 拿到证书之后:拼链、转格式、校验
签发方给回来的通常是叶子证书,中间证书要单独下载,或者给一个已经包含中间证书的 fullchain 文件。如果没有,就手工拼:
cat server.crt intermediate.crt > fullchain.pem拼接顺序不能反。有些签发方给的中间证书文件里带注释文字,某些服务端解析会报错,用之前先grep -v "^#"清一遍。
格式转换也很常见:Java 生态要 JKS 或 PKCS12,Windows 服务要 PFX。
# PEM 转 PKCS12(含私钥和完整链) openssl pkcs12 -export -out server.pfx \ -inkey example.key -in fullchain.pem \ -name "example" -passout pass:改成你的口令 # 校验私钥和证书是否配对,两者模数一致才算匹配 openssl x509 -noout -modulus -in server.crt | openssl md5 openssl rsa -noout -modulus -in example.key | openssl md5这两条校验命令建议每次都跑。私钥和证书不匹配导致的启动失败,排查起来非常费时间,而预先校验只要三秒。ECDSA 证书不能用-modulus,改用比对公钥:
openssl x509 -in server.crt -noout -pubkey | openssl md5 openssl pkey -in example.key -pubout | openssl md54. 服务端落地:Nginx 与 Apache 的配置细节
4.1 Nginx 一份能直接抄的配置
下面这段是我在多数业务上用的基线,逐行解释在注释里:
server { listen 443 ssl; http2 on; server_name www.example.com; # 叶子 + 中间证书,顺序不能反 ssl_certificate /etc/ssl/example/fullchain.pem; ssl_certificate_key /etc/ssl/example/example.key; # 双证书:椭圆曲线优先,RSA 兜底老客户端 ssl_certificate /etc/ssl/example/fullchain-ec.pem; ssl_certificate_key /etc/ssl/example/example-ec.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:20m; ssl_session_timeout 1d; ssl_session_tickets off; ssl_stapling on; ssl_stapling_verify on; ssl_trusted_certificate /etc/ssl/example/chain.pem; resolver 127.0.0.1 valid=300s; add_header Strict-Transport-Security "max-age=31536000" always; }几个点值得单独说。ssl_prefer_server_ciphers off是刻意的,现代实践下让客户端优先选择更合适,因为客户端最清楚自己环境的性能特征。ssl_session_tickets off是因为静态票据密钥在部分场景下会削弱前向保密,改由服务端缓存会话更可控。
ssl_trusted_certificate指向的是不含叶子证书的链文件,它只服务于 OCSP Stapling 的验签,和ssl_certificate用途不同,别混用。
Apache 那边对应的是:
SSLEngine on SSLCertificateFile /etc/ssl/example/fullchain.pem SSLCertificateKeyFile /etc/ssl/example/example.key SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256 SSLHonorCipherOrder off注意 Apache 老版本的SSLCertificateChainFile已废弃,新版直接把中间证书拼进SSLCertificateFile即可。如果你在维护旧环境,两个指令并存可能导致链被重复下发。
4.2 协议版本与加密套件怎么定才不背锅
停用 SSLv3、TLS 1.0、TLS 1.1 基本是底线,这几个版本存在已知的协议层缺陷,主流浏览器也早就不支持了。现在配TLSv1.2 TLSv1.3就够。
加密套件这块,TLS 1.3 的套件是固定的,服务端配置管不着,也不用管,它本身就只保留了 AEAD 类和前向保密的密钥交换。真正要配的是 TLS 1.2 那一层,原则有三条:只留 ECDHE 做密钥交换(保证前向保密)、只留 AEAD 类加密(GCM 或 ChaCha20-Poly1305)、签名算法让客户端自己挑。
这里有笔账可以算清楚为什么推荐椭圆曲线。一次 RSA 2048 握手,证书加签名大概要传 1.5KB 左右;换成 ECDSA P-256,证书和签名加起来能省掉一半以上。看着不多,但移动端弱网下首包延迟的差异是能感知的。而且服务端的签名运算开销下降明显,高并发握手场景下 CPU 占用会好看很多。
提示:改完套件配置后,一定要用真实的老设备测一遍,特别是那些还停留在几年前系统版本的机型。理论上限砍太狠带来的兼容问题,往往要等到线上才暴露。
4.3 HSTS 与 OCSP Stapling:锦上添花还是埋雷
HSTS 让浏览器记住“这个域名只走 HTTPS”,下次直接跳过 HTTP 请求。它防的是 SSL 剥离类攻击,价值很实在。但它有个非常危险的特性:一旦下发,在有效期内无法撤销。如果误配了includeSubDomains,而某个子域其实还没上 HTTPS,那个子域在有效期内会彻底打不开。
我的做法是分阶段:第一轮先设max-age=300,观察几天日志确认所有子域都正常;第二轮提到一天;确认无误后,再上一年并考虑includeSubDomains。preload这个选项除非你有极强的运维保障,否则不要碰,进了预加载列表退出流程很漫长。
OCSP Stapling 是服务端代客户端去查证书吊销状态,把结果随握手一起发给客户端。好处是省掉了客户端的一次额外查询,握手更快,也避免吊销状态查询本身泄露用户访问了哪个站点。
配置它要注意两点:一是必须配resolver,服务端得能解析签发方的吊销查询地址;二是首次握手时缓存是空的,服务端会异步去拉,所以配完立刻测可能看不到 stapled 响应,等几分钟再测才准。另外这个特性依赖服务端能出网,纯内网环境配了也是白配,还可能因为超时拖慢握手,这种情况直接关掉更合适。
还有一个趋势值得留意:吊销状态查询这套机制在近年被逐步弱化,取而代之的是更短的证书有效期。所以如果你的证书已经是 90 天以内的短周期,OCSP 的收益就没那么大了,别为它耗费太多调试时间。
4.4 自动续期与不中断重载
续期脚本的核心不止是“拿到新证书”,还有“让新证书立刻生效且不丢连接”。顺序应该是:写入新文件(先写临时文件再原子替换,避免服务读到半个文件)→ 校验新证书有效期和域名 → 重载服务 → 回读线上证书确认指纹已变。
# 原子替换 + 平滑重载的骨架 cp new/fullchain.pem /etc/ssl/example/fullchain.pem.tmp mv /etc/ssl/example/fullchain.pem.tmp /etc/ssl/example/fullchain.pem nginx -t && nginx -s reloadnginx -t这一步不能省。配置语法错了直接 reload 会失败,而如果先 reload 再检查,服务可能已经在用错误配置了。原子替换里的mv在同一文件系统内是原子操作,这是保证不出现“读到半个文件”的关键,直接用cp覆盖有极小概率踩到。
对于多机部署,别让每台机器各自去签发,容易触发签发方的频率限制。我的做法是一台机器负责签发,把结果同步到其他节点,同步完成后再依次 reload,一台一台来,避免同时重载造成短暂的服务抖动。
5. 排查实录:线上最常见的十种证书故障
5.1 现象—根因对照表
排查这件事,经验比工具重要。下面这张表是我这些年攒下来的高频问题清单,基本覆盖了日常遇到的绝大多数情况:
| 报错或现象 | 根因 | 处理动作 |
|---|---|---|
certificate has expired | 证书过期或系统时间偏差 | 续期 / 校准时间同步 |
unable to get local issuer certificate | 中间证书缺失 | 补全链文件 |
hostname mismatch | SAN 未覆盖当前域名 | 重新签发含该域名 |
NET::ERR_CERT_AUTHORITY_INVALID | 用了自签证书给公网 | 换公共签发证书 |
| 只在部分机型失败 | 协议或套件过新,或链不全 | 放宽协议、补链 |
| 握手超时 | 服务端出网受限、吊销查询卡住 | 关闭 Stapling |
| 页面能开但提示不安全 | 混合内容 | 页面资源全站改 HTTPS |
| 改完配置后无限重定向 | 跳转规则与前置节点冲突 | 检查转发层协议头 |
| 一张证书多域名只有部分可用 | CSR 提交域名不全 | 重签并核对 SAN 列表 |
| 浏览器正常、程序调用报错 | 程序未加载系统信任库 | 显式指定 CA 路径 |
表格里的最后一条特别值得说。很多人用命令行工具或某种语言的 HTTP 客户端调接口,报证书错误,但浏览器打开完全正常。这通常是因为该运行环境有自己的信任库,没读系统的那份。处理方式是把根证书或完整链显式配置给客户端,而不是简单粗暴地关掉校验——关校验这种做法一旦流进生产代码,后患无穷。
5.2 三把命令行武器
手上常备三条命令,能解决八成的定位工作。
第一条,看服务端到底下发了什么:
openssl s_client -connect www.example.com:443 -servername www.example.com -showcerts < /dev/null重点看输出里的Certificate chain部分有几张证书,以及最后的Verify return code是不是 0。少于两张(叶子加中间)基本可以断定链不全。-servername这个参数不能省,多域名共用同一 IP 的场景下,不带它就是拿默认证书,测出来的结果会误导你。
第二条,看证书本身的字段:
openssl x509 -in fullchain.pem -noout -text | grep -A2 "Subject Alternative Name" openssl x509 -in fullchain.pem -noout -dates第一条确认域名覆盖,第二条确认有效期。签发时把域名漏掉是高频事故,尤其是从多个站点迁移域名的时候。
第三条,模拟完整请求看握手细节:
curl -vI https://www.example.com --resolve www.example.com:443:1.2.3.4--resolve可以绕过本地 DNS 直接打到指定 IP,用来验证某台具体机器上的证书对不对,特别适合多机部署逐步切换的场景。
5.3 混合内容与重定向回环这类“伪证书问题”
有两类问题经常被误判成证书问题,其实跟证书一点关系没有。
混合内容:主页面是 HTTPS,但里面引用了 HTTP 的图片、脚本、样式,浏览器就会把锁图标标灰或者标红。这种情况证书完全没问题,问题在页面资源。排查方法是打开开发者工具看控制台里的混合内容警告,通常能直接列出具体 URL。修复上优先改源头,把资源地址换成 HTTPS 或协议相对写法;CSS 里内嵌的url()和 JS 动态拼接的地址最容易被漏掉。
重定向回环:现象是访问直接报“重定向次数过多”。根因通常是链路上有多个节点都在做 HTTPS 跳转,而且彼此不认对方的协议标识。比如前置转发层已经终止了加密并把请求以 HTTP 转给后端,后端看到是 HTTP 又跳一次 HTTPS,绕回前置层再跳,死循环。解决办法是让后端识别转发层带过来的协议头,只在确实是 HTTP 请求时才跳转,同时确保跳转规则不会把验证路径也一起跳掉。
我在实际处理中最常用的一招是:先在浏览器里用无痕窗口走一遍,把网络面板的重定向链条看清楚,每一步的响应码和 Location 都记下来,问题基本就定位到具体哪个节点了。
6. 进阶场景:内网自签、双向认证与握手性能
6.1 自建 CA 给内网发证
内网服务用公共签发方很不方便——域名不对外、验证通道走不通、还要把内部主机名暴露出去。这时候自建一套内部 CA 更合适。
流程是:先生成 CA 私钥和自签的 CA 证书,再用它给内部服务签发叶子证书,把叶子证书和 CA 证书一起下发给客户端,客户端导入 CA 证书到信任库即可。关键是不要把 CA 私钥放在业务机器上,它一旦泄露,整套内部信任体系就废了。我的习惯是把它放在离线环境或者权限极严的专用机器上,签发时人工介入或通过受控流程调用。
实用提醒:内部 CA 的根证书有效期可以设长一些(十年),但叶子证书一定要短(90 天左右),并且同样做自动化续期。很多人觉得内网无所谓,结果三年后一堆机器上的证书同时过期,那场面比公网证书过期还难看,因为涉及的服务往往更多、更分散。
6.2 mTLS 双向认证落地要点
普通 HTTPS 只验证服务端身份,客户端是谁服务端并不知道。mTLS 加上反向验证:客户端也要出示证书,服务端校验通过才放行。适合服务间调用、内部 API 网关、设备接入这类场景。
Nginx 侧配置不复杂:
ssl_client_certificate /etc/ssl/internal/ca.crt; ssl_verify_client on; ssl_verify_depth 2;ssl_verify_depth要设对,链上有几级中间证书就设几,设小了会导致合法证书验证失败。
实际落地有三个坑要提前想。第一,客户端证书的吊销怎么做,一旦某台机器的私钥泄露要能立刻踢掉,这需要维护吊销列表并定期更新,或者改用短周期客户端证书。第二,客户端证书里最好带上身份标识,方便后端做鉴权,否则你只知道“这是个合法客户端”,不知道“这是谁”。第三,要注意客户端证书的续期同样需要自动化,否则到期那天所有调用方一起挂。
6.3 握手开销与优化:会话复用、TLS 1.3、HTTP/2
握手是有成本的,尤其是首次连接,需要额外一到两个往返。对高并发短连接场景,这个成本不能忽略。三个优化方向按性价比排序。
上 TLS 1.3。它把完整握手压到一次往返,会话恢复甚至可以做到零往返。同时它砍掉了大量历史包袱,配置面更小。只要客户端环境不是特别老,直接开。
开会话复用。会话缓存让客户端第二次连接时跳过完整的密钥协商。服务端用共享缓存,多进程之间共享,容量按并发连接数的百分之一到五估算,二十兆的共享缓存通常能扛住相当规模的流量。会话票据也能用,但要注意轮换票据密钥,静态密钥会削弱前向保密。
上 HTTP/2。它本身不改变证书逻辑,但复用同一条加密连接跑多个请求,等于把握手成本摊薄了。再往后的新版本协议基于不同的传输层,配置方式和证书体系基本一致,但部署形态差异较大,属于另一个话题了。
优化的前提是先测。别凭感觉调参,先看握手耗时分布,再看 CPU 在握手阶段占多少,确认瓶颈真的在握手再动手,否则很容易优化了个寂寞。
最后分享一个我自己的习惯:把证书到期时间做成监控指标,阈值设在三十天和七天各告警一次。三十天那次是提醒你自动化可能出问题了,七天那次就是真要动手了。这个监控比任何复杂的排查手段都管用,因为证书类故障里最不该发生的就是“忘了续”。