最容易让人误判的HTTPS故障是:CDN控制台显示"新证书已部署",源站目录里的fullchain.pem也换了,但浏览器打开网站,看到的还是旧证书。此时先别急着清缓存,更不要连续reload几遍碰碰运气。证书到底在哪里终止、请求实际落到了哪个边缘节点,往往比源站文件内容更关键。
本文用example.com作占位域名,目标不是绑定某一家CDN,而是建立一套可以迁移到CDN、WAF、四层负载均衡和Nginx的排查顺序。
一、先画清楚TLS终止点
访问链路可能是"浏览器→CDN→WAF→负载均衡→Nginx",也可能是浏览器直接连Nginx。CDN/WAF或七层代理可以终止TLS;四层透传入口本身通常不解密TLS。浏览器看到的证书,只来自它实际连接的第一处TLS终止点;源站换证书,不会自动改变已经配置在边缘节点上的证书。

这里有个很常见的坑:后台的"源站证书"与"边缘证书"是两个对象。前者用于CDN回源校验,后者用于给访客握手。先确认访客侧证书由谁提供,再决定去哪个控制台查版本。
二、DNS解析先确认请求去了哪里
先记录当前解析结果,不要只凭浏览器地址栏猜测:
dig +short example.com A
dig +short example.com AAAA
dig +short example.com CNAME
curl -vI https://example.com/
如果A和AAAA分别指向不同入口,浏览器可能优先走IPv6,于是你更新的是IPv4入口,实际验收却落到另一套证书。使用 curl -4 和 curl -6 分别测试,避免为排查问题关闭生产IPv6。解析结果会受TTL和本地缓存影响,所以要保留查询时间和结果。
三、用SNI确认拿到的就是目标域名证书
TLS握手时,客户端会通过SNI告诉服务端要访问的主机名。没有SNI,或SNI写错,服务端可能返回默认站点证书。OpenSSL测试时必须显式带上-servername:
openssl s_client -connect example.com:443 \
-servername example.com -verify_hostname example.com \
-verify_return_error -showcerts </dev/null
保留完整握手日志;需要解析SAN时,另用 openssl x509 -in leaf.pem -noout -ext subjectAltName 读取保存的叶子证书。在输出中重点看subject、issuer、Not Before、Not After和SAN。不要只看证书文件名,也不要只看浏览器锁标志;文件名可以不变,证书序列号和有效期才是实际身份。
四、固定边缘IP复测,判断是否节点未同步
如果DNS返回多个地址,可以使用curl --resolve逐个测试。它会把域名解析临时固定到指定IP,同时仍把正确的Host和SNI传给服务端:
curl -vI --resolve example.com:443:203.0.113.10 \
https://example.com/
openssl s_client -connect 203.0.113.10:443 \
-servername example.com </dev/null \
| openssl x509 -noout -serial -dates -subject
上面的 curl 保留默认信任与主机名校验,不使用 -k;openssl 管道仅提取证书,不能把末端 x509 返回成功当作整个握手通过。把每个IP的证书序列号、Not After和issuer放在一起比较。如果部分节点是新证书、部分节点是旧证书,问题更像边缘发布未完成、版本绑定不一致或多入口配置遗漏;这时清浏览器缓存没有帮助。应回到边缘证书的发布记录,确认目标域名、版本和覆盖范围。
五、区分TLS证书缓存与HTTP内容缓存
HTTP缓存保存的是响应内容,通常不会决定新的TLS握手返回哪张证书。浏览器复用已有连接时,短时间内也可能看不到新的握手,但新开连接或用命令行复测可以区分这两类现象:
curl -vI --connect-timeout 10 --max-time 20 https://example.com/
curl -v --http1.1 https://example.com/ -o /dev/null
如果新连接已经返回新证书,只是页面内容旧,排查Cache-Control、缓存键和刷新策略;如果新连接仍返回旧证书,继续查TLS终止点、证书绑定和边缘节点,不要把HTTP缓存当成万能锅。
六、源站Nginx只在它确实终止TLS时检查
当CDN以HTTPS回源,源站Nginx通常仍需要一张供回源握手使用的证书。检查配置和证书内容:
sudo nginx -T | grep -nE 'listen .*443|server_name|ssl_certificate'
sudo openssl x509 -in /etc/nginx/ssl/fullchain.pem \
-noout -serial -dates -subject -issuer
sudo nginx -t && sudo systemctl reload nginx
nginx -t只说明配置语法和引用文件可用,reload也只影响这台Nginx。若访客侧TLS在CDN已经终止,源站这一步通过并不能证明公网证书已更新。别急着reload:先确认文件路径、权限、证书与私钥匹配,再执行配置检查。
七、用证书序列号建立版本证据
有效期相同或证书由同一CA签发时,肉眼比较标题并不可靠。建议把新旧版本的序列号、指纹和有效期记录下来:
openssl x509 -in new.pem -noout -serial -fingerprint -sha256 -dates
openssl s_client -connect example.com:443 \
-servername example.com </dev/null \
| openssl x509 -noout -serial -fingerprint -sha256 -dates
本地文件与公网握手结果一致,说明"证书内容已经签发并且某个入口已生效";从一个出口固定IP不能枚举Anycast背后的全部物理节点,还需结合CDN分发记录及多地域探测。已覆盖入口全部一致,才接近"全链路发布完成"。这两个结论不要混为一谈。
八、常见现象与处理方向
| 现象 | 优先检查 | 不要先做 |
|---|---|---|
| 所有节点都是旧证书 | 边缘证书绑定、发布状态、域名覆盖 | 反复清浏览器缓存 |
| 部分节点新、部分节点旧 | 多节点发布、多个入口和DNS结果 | 只测一次就下结论 |
| IPv4新、IPv6旧 | AAAA记录和IPv6入口配置 | 只看A记录 |
| 源站新、公网旧 | TLS终止点是否在CDN/WAF | 继续改源站证书 |
| 证书新但页面旧 | HTTP缓存和长连接 | 把内容缓存当证书缓存 |
九、最后用一张验收表收口

- 记录A、AAAA、CNAME及查询时间,确认IPv4和IPv6入口没有漏测。
- 用正确SNI测试每个入口,保存证书序列号、SHA-256指纹和有效期。
- 分别核对边缘证书版本、发布状态、域名覆盖范围和回源证书。
- 用新连接区分TLS证书问题与HTTP内容缓存问题。
- 源站改完后执行
nginx -t,再reload;公网结果必须重新用OpenSSL或curl读回。
证书发布不是"上传文件"这个动作,而是从签发、绑定、分发到握手验收的一条链。把TLS身份和HTTP内容分开验证,旧证书这个小幽灵通常就没地方藏了。