排查Nginx SSL握手协议不匹配,核心是看日志定方向、抓错误码定原因、用工具验实际:先据error.log中“to upstream”或“client: xxx”锁定是代理后端还是服务端接收失败;再依OpenSSL错误码(如1408F10B、14209102、1417A0C1等)精准定位协议/套件不兼容;最后用openssl s_client实测两端真实支持的TLS版本与密码套件,针对性调整配置。

排查 Nginx 中 SSL 握手协议不匹配,核心是“看日志定方向、抓错误码定原因、用工具验实际”。别一上来就改配置,90% 的问题在 error.log 里那句 while SSL handshaking 后面跟着的关键词已经指明了路径。
第一步:从 error.log 锁定握手方向
打开 /var/log/nginx/error.log,找到紧挨着 SSL_do_handshake() failed 的上下文:
- 出现 while SSL handshaking to upstream → Nginx 是 HTTPS 客户端,问题出在
proxy_pass https://连后端时协议不匹配(比如后端其实是 HTTP) - 出现 while SSL handshaking, client: xxx.xxx.xxx.xxx(无 upstream)→ Nginx 是 HTTPS 服务端,问题出在浏览器或 App 连你时,TLS 版本或密码套件没交集
第二步:识别关键 OpenSSL 错误码
错误码比文字描述更准。重点关注以下几类:
- error:1408F10B (wrong version number) → 协议错位:Nginx 用 https:// 去连一个只监听 HTTP 的后端;或客户端把 HTTP 请求发到了 HTTPS 端口
-
error:14209102 (unsupported protocol) → TLS 版本不兼容:例如
proxy_ssl_protocols TLSv1.2,但后端只支持 TLSv1.3;或服务端配了ssl_protocols TLSv1.3,而客户端(如旧版 Android)只支持 TLSv1.2 -
error:1417A0C1 (no shared cipher) → 密码套件无交集:服务端
ssl_ciphers过严,或客户端太老(如 IE11/Win7 默认不支持 AES-GCM) - error:1417D18D (version too low) → 客户端 TLS 版本低于服务端允许的最低版本(如 Nginx 只开 TLSv1.2+,但客户端仅支持 TLSv1.0)
第三步:用 openssl 实测真实协商结果
绕过 Nginx 配置,直接验证两端能力:
- 查服务端实际支持的协议和套件:
echo | openssl s_client -connect yourdomain.com:443 -tls1_2 -servername yourdomain.com 2>/dev/null | grep "Protocol\|Cipher"echo | openssl s_client -connect yourdomain.com:443 -tls1_3 -servername yourdomain.com 2>/dev/null | grep "Protocol\|Cipher" - 查证书链是否完整下发:
echo | openssl s_client -connect yourdomain.com:443 -showcerts -servername yourdomain.com 2>/dev/null→ 看输出中是否包含全部证书,且末尾证书能回溯到可信根 - 模拟老旧客户端(如只支持 TLSv1.2 + SHA1 套件):
openssl s_client -connect yourdomain.com:443 -cipher 'ECDHE-RSA-AES128-SHA' -tls1_2
第四步:针对性调整配置
根据验证结果精准干预,不盲目放宽:
- 若确认是协议不兼容:检查
ssl_protocols或proxy_ssl_protocols是否排除了客户端唯一支持的版本;必要时临时加回TLSv1.2(不推荐加 TLSv1.0/1.1) - 若确认是套件无交集:在保留前向保密前提下,补充兼容性套件,例如:
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256';
如需兼容旧设备,可追加ECDHE-RSA-AES128-SHA,但确保含!MD5 !RC4 !EXPORT !aNULL - 若问题出在
to upstream场景:先用curl -v http://backend:port和curl -v https://backend:port确认后端真实协议;再检查是否漏了proxy_ssl_server_name on;(尤其后端是多域名 HTTPS 服务时)


















