排查nginx ssl握手报错核心是先定方向:日志含“to upstream”则查proxy_pass协议匹配、sni启用及会话复用;含“client: xxx”则查证书链、tls版本与密码套件兼容性,并结合openssl错误码精准定位协议层问题。

排查 Nginx SSL 握手报错,核心是“先定方向、再看错误码、最后验协议”。别一上来就重配证书或调超时——90% 的问题卡在日志里那句 to upstream 或 client: xxx 上。
第一步:从 error.log 锁定握手方向
打开 /var/log/nginx/error.log,找紧挨着 SSL_do_handshake() failed 的上下文:
- 出现
while SSL handshaking to upstream→ Nginx 是 HTTPS 客户端,问题出在proxy_pass https://...连后端失败 - 出现
while SSL handshaking, client: xxx.xxx.xxx.xxx(无 upstream)→ Nginx 是 HTTPS 服务端,问题出在浏览器/APP 连你这台服务器时失败
方向错了,所有后续操作都白忙。线上绝大多数故障属于前者——后端协议写反、SNI 没开、会话复用错乱。
第二步:对照 OpenSSL 错误码直击协议层
错误码比文字描述更准。常见几个关键码及其含义:
-
error:1408F10B(wrong version number)→ 后端根本没开 HTTPS,Nginx 却用https://去连;或客户端把 HTTPS 请求发到了 HTTP 端口 -
error:14094410(handshake failure)→ 多见于 SNI 缺失(后端多域名 HTTPS 服务未收到 server_name),或密码套件无交集 -
error:14209102(unsupported protocol)→ TLS 版本不兼容,比如proxy_ssl_protocols只开了 TLSv1.2,但后端只支持 TLSv1.3 -
error:141CF06C(bad key share)→ TLS 1.3 密钥交换失败,常见于 Nginx 新 + 后端 JDK 8u2XX 以下 / OpenSSL 旧 -
error:14094085(ccs received early)→proxy_ssl_session_reuse on复用了已失效的会话(如后端重启、证书轮换、负载节点不一致)
第三步:按方向分场景快速验证
如果是 to upstream 类型:
- 用
curl -v http://ip:port和curl -v https://ip:port分别测试,确认后端真实监听协议 - 若后端是多域名 HTTPS(如 K8s Ingress、云 API),必须加:
proxy_ssl_server_name on;proxy_ssl_name "api.example.com";proxy_set_header Host "api.example.com"; - 临时关闭会话复用:
proxy_ssl_session_reuse off;,观察是否还报ccs received early
如果是 client: xxx 类型:
- 检查证书链完整性:
openssl s_client -connect yourdomain.com:443 -showcerts,确认能回溯到可信根 - 验证 TLS 版本与套件是否兼容:
openssl s_client -connect yourdomain.com:443 -tls1_2 -cipher 'ECDHE-RSA-AES128-SHA',逐个试老套件 - 确认未禁用客户端唯一支持的协议(例如只写了
ssl_protocols TLSv1.3;,但 Android 7 设备只支持 TLSv1.2)
第四步:必要时抓包确认真相
日志和配置都对,仍不稳定?用 tcpdump 抓 TLS 握手阶段最可靠:
- 过滤 TLS 握手包:
tcpdump -i any 'tcp port 443 and (tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x160301)' - 重点看 ClientHello 发出后,ServerHello 是否在
ssl_handshake_timeout内返回;若没返回,说明卡在中间链路(WAF、防火墙、网络抖动)或后端未响应
不复杂但容易忽略。











