nginx ssl握手失败本质是tls协议协商中断,需先据日志中“to upstream”或“client: xxx”区分方向:前者查proxy_pass协议匹配、sni启用及会话复用;后者查证书链、tls版本与密码套件兼容性。

这个错误不是客户端“假死”,而是服务端在 TLS 握手中途主动断连,本质是协议协商失败导致的强制终止。关键要跳过“重试SSL版本”这类表面操作,直接定位哪一环没谈拢。
看日志上下文,先分清谁是客户端、谁是服务端
错误行本身不说明方向,必须结合紧邻的上下文判断:
- 出现 while SSL handshaking to upstream → Nginx 是客户端,问题出在它连后端(如 Java 服务、Kibana、云 API)时失败
- 出现 client: xxx.xxx.xxx.xxx 且无 upstream 字样 → Nginx 是服务端,问题出在浏览器、App 或脚本连你这台服务器时失败
- 若同时出现 SNI 信息(如 SSL server name: "api.example.com"),说明客户端发了 Server Name,但服务端没响应或拒绝
抓包确认是否真握手中断,而非网络丢包
用 tcpdump 过滤 TLS 握手起始帧,避免被重传或超时干扰:
- 运行:tcpdump -i any 'tcp port 443 and (tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x160301)'(捕获 ClientHello)
- 观察后续:如果只看到 ClientHello → ServerHello,紧接着就是 RST 或 FIN,没有 Certificate 或 ServerHelloDone,就是典型的协商失败
- 如果 ClientHello 后完全没收到任何响应,可能是防火墙拦截、目标端口未开或路由不通
用 OpenSSL 模拟客户端,逐层验证兼容性
别猜,用命令直连测试,快速暴露断点:
- 测协议版本:openssl s_client -connect target.com:443 -tls1_2 -servername target.com,依次换 -tls1、-tls1_1、-tls1_3
- 某版本返回 Verify return code: 0 (ok) 且有完整握手日志 → 该版本可用;若报 handshake failure 或直接退出 → 服务端禁用该版本
- 查套件交集:openssl s_client -connect target.com:443 -cipher 'ALL:eNULL' 2>/dev/null | grep "Cipher is",无输出说明当前协议下无可用套件
重点检查中间链路和配置陷阱
很多问题不出在两端,而在中间:
- Nginx 反向代理到 HTTPS 后端时,漏配 proxy_ssl_server_name on → 多域名共享 IP 的后端无法识别请求归属,直接拒连
- 负载均衡器(如 AWS ALB、F5)或 WAF 强制启用 TLSv1.3,但客户端只支持 TLSv1.2 → 握手在 ServerHello 后立即 RST
- 企业出口代理开启 SSL Inspection,但证书信任链不全或 OCSP 响应超时 → 客户端查证书状态失败后主动断开
- JDK 应用作为客户端时,JDK 7u111 以下默认不支持 TLSv1.2,JDK 8u291 以下对 TLSv1.3 支持不稳 → 日志里看似服务端断连,实为客户端能力不足











