Nginx “SSL_do_handshake() failed”本质是TLS握手被硬性中断,须先据日志含“to upstream”或“client: xxx”区分方向:前者查proxy_pass协议匹配、SNI启用及会话复用;后者查证书链、TLS版本与密码套件兼容性。

这个报错不是配置写错那么简单,而是 TLS 协议栈在握手阶段被硬性中断。关键第一步:看日志里带不带 to upstream ——方向错了,所有排查都白费。
先盯死日志关键词,锁定问题方向
错误日志里紧挨着 SSL_do_handshake() failed 的上下文,决定你该查哪边:
- 出现 while SSL handshaking to upstream → Nginx 主动连后端 HTTPS 服务失败,重点查 proxy_pass 配置、SNI、会话复用
- 出现 while SSL handshaking, client: xxx.xxx.xxx.xxx(没 upstream)→ 客户端连 Nginx 失败,重点查证书链、TLS 版本、密码套件兼容性
代理到后端失败的三大高频根因
线上绝大多数 “to upstream” 类故障,都卡在这三处:
-
协议错配:proxy_pass 写成 https://,但后端实际只监听 HTTP(比如 Spring Boot 默认 8080)。Nginx 强行发 TLS 握手包,对方回乱码或直接断连,日志报 wrong version number。验证方法:用
curl -v http://ip:port和curl -v https://ip:port分别试通哪个 - SNI 缺失:后端是多租户 HTTPS 服务(如云 API、K8s Ingress),必须靠 SNI 告诉它连哪个域名。Nginx 默认不发,需加 proxy_ssl_server_name on;,否则报 sslv3 alert handshake failure
- 会话复用错乱:proxy_ssl_session_reuse on(默认开启)会让 Nginx 复用旧会话 ID 连上游。一旦上游重启、换证书或负载节点不一致,复用的会话就失效,容易触发 ccs received early
看懂 OpenSSL 错误码,直击协议层问题
错误码才是真正的线索,不能只看文字描述:
- error:141CF06C(bad key share)→ TLS 1.3 密钥交换失败,常见于 Nginx 新、后端 OpenSSL 或 JDK 太老(如 JDK 8u2XX 以下)
- error:14094410(handshake failure)→ 双方无共同密码套件,或服务器要求客户端证书但未提供
- error:14209102(unsupported protocol)→ TLS 版本不兼容,比如客户端只支持 TLS 1.3,而 proxy_ssl_protocols 只开了 TLS 1.2
- error:1408F10B(wrong version number)→ 数据根本不像 TLS 流,基本可断定是协议写反了
实操建议:快速验证与稳妥修复
别一上来就改一堆配置,按顺序做这几步:
- 确认后端真实协议:用 curl 直连后端地址,看 HTTP/HTTPS 哪个能返回健康检查响应
- 若后端是 HTTPS 且依赖域名,必须加 proxy_ssl_server_name on;;如果 upstream 是 IP,再补上 proxy_ssl_name "your-api-domain.com";
- 遇到 ccs received early 或连接不稳定,先临时关掉会话复用:proxy_ssl_session_reuse off;,验证是否缓解
- 调 TLS 版本和密码套件前,先用
openssl s_client -connect host:port -tls1_2等命令手动测试握手路径


















