这是针对HTTPS证书兼容性的渐进式验证,即在已启用HTTPS的服务中,通过分流部分流量使用新CA证书,观察客户端握手是否成功,以提前暴露老旧系统、嵌入式设备等对根证书信任链支持不足的问题。

这不是常规意义上的灰度发布,而是针对HTTPS证书兼容性做的渐进式验证——即:在已启用 HTTPS 的服务中,用新 CA 颁发的证书替换旧证书时,不一次性全量切换,而是先让部分流量(如特定用户、IP 或路径)使用新证书,观察客户端握手是否成功、是否报“证书不受信”“NET::ERR_CERT_AUTHORITY_INVALID”等错误。
为什么需要对 CA 证书做灰度上线?
不同客户端(尤其是老旧系统、嵌入式设备、某些安卓版本、企业内网代理)对根证书信任链的支持程度差异很大。即使新证书本身合规,若其签发 CA 不在目标客户端的根证书库中,就会导致 HTTPS 连接失败。全量切换可能引发大面积访问异常,而灰度可提前暴露兼容性问题。
核心思路:按请求分流,动态加载不同 SSL 配置
Nginx 本身不支持单个 server 块内“按条件切换 ssl_certificate”,但可通过以下两种方式实现等效效果:
-
方案一:多 server 块 + 请求特征路由(推荐)
定义两个监听 443 的 server 块,分别绑定旧证书和新证书;再用前置 HTTP 80 端口或上游负载层(如另一台 Nginx)根据 Cookie/IP/参数判断,将请求 307 临时重定向到对应 HTTPS 域名(如 old.example.com / canary.example.com),两个域名共用同一后端,仅证书不同。 -
方案二:利用 map + ssl_certificate_by_lua*(需 OpenResty)
若已部署 OpenResty,可用ssl_certificate_by_lua_block在 TLS 握手早期阶段动态选择证书文件路径。例如:map $arg_cert_mode $cert_path { "new" "/etc/nginx/ssl/new-ca.crt"; default "/etc/nginx/ssl/old-ca.crt"; }
再在 server 块中调用 Lua 脚本读取$cert_path并 set_cert / set_priv_key。
实操建议:从最小风险场景开始验证
优先选择以下几类流量作为灰度入口,便于快速定位问题:
- 内部员工访问(通过 IP 段匹配,如
if ($remote_addr ~ "^10\.10\.") { return 307 https://canary.example.com$request_uri; }) - 带特定测试 Header 的请求(如
X-Test-Cert: true),方便开发和测试人员主动触发 - 特定 URL 路径(如
/health/cert-test),专用于自动化探活与证书链抓包分析
务必配合客户端日志采集(如浏览器 console、curl -v 输出、移动端 SDK 上报的 SSL 错误码)和 Nginx error_log 中的 SSL_do_handshake() failed 记录,重点关注 TLS 握手阶段的 failure reason。
回滚必须秒级完成
一旦发现某类设备批量失败(如 iOS 14.2 下所有 Safari 报错),应立即停止灰度流量,并恢复为全量旧证书配置。最佳实践是把证书路径抽成变量(如 set $ssl_cert_file "/etc/nginx/ssl/old.crt";),reload 前仅修改该行并执行 nginx -t && nginx -s reload,避免重写整个配置。


















