先看日志关键词锁定方向:含“to upstream”为Nginx连后端失败,查proxy_pass协议匹配、SNI启用(proxy_ssl_server_name on)及会话复用;含“client: xxx”为客户端连Nginx失败,查证书链完整性、TLS版本与密码套件兼容性。

遇到 Nginx 报“SSL_do_handshaking() failed”,别急着调参数或换证书——先看日志里那句错误紧挨着的关键词,方向错了,所有操作都白费。
第一步:看日志,锁定是“谁连谁”失败
打开 /var/log/nginx/error.log,重点找这行错误前后的上下文:
- 出现 while SSL handshaking to upstream → Nginx 主动连后端 HTTPS 服务失败(比如 proxy_pass https://api.example.com)
- 出现 while SSL handshaking, client: xxx.xxx.xxx.xxx → 外部客户端(浏览器、APP)连 Nginx 失败(server 块的 SSL 配置出问题)
线上绝大多数情况属于前者,但很多人一上来就查自己证书,绕远路。
第二步:代理到后端失败,重点查三件事
确认是 to upstream 类错误后,聚焦以下高频根因:
-
协议写反了:proxy_pass 写成 https://,但后端实际只监听 HTTP(如 Spring Boot 默认 8080)。验证方法:
curl -v http://127.0.0.1:8080能通,curl -v https://127.0.0.1:8080直接报错,基本就是它 -
SNI 没开:后端是多域名 HTTPS(如 K8s Ingress、云 API),必须加
proxy_ssl_server_name on;;若用 IP 或泛域名,还得补proxy_ssl_name "api.example.com"; -
会话复用错乱:默认开启的
proxy_ssl_session_reuse on;在后端重启、证书轮换或负载节点不一致时容易触发ccs received early。临时关闭可快速验证:proxy_ssl_session_reuse off;
第三步:看 OpenSSL 错误码,比文字更准
错误日志里紧跟着的十六进制码才是关键线索:
-
error:14094410→ handshake failure,大概率缺 SNI 或密码套件无交集 -
error:1408F10B→ wrong version number,基本是协议错配(HTTPS 配给 HTTP 后端) -
error:141CF06C→ bad key share,TLS 1.3 密钥交换失败,常见于后端 OpenSSL 或 JDK 版本太老(如 JDK 8u2XX 以下) -
error:14094085→ ccs received early,会话复用与后端 TLS 实现不兼容
第四步:客户端连 Nginx 失败,查证书与协议兼容性
若日志显示 client: xxx,问题在 server 块配置:
- 用
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com -showcerts看是否完整下发证书链(含中间 CA) - 检查
ssl_protocols是否至少包含 TLS 1.2;若需兼容老旧客户端,可加 TLS 1.1,但不建议开 TLS 1.0 - 确认
ssl_ciphers有足够宽泛的套件,避免只留高安全但老旧客户端不支持的组合(如仅 ECDHE-ECDSA-AES256-GCM-SHA384) - 时间不同步也会导致握手失败,检查服务器系统时间是否准确(
date和ntpq -p)


















