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

“SSL handshake failed”本身不是日志级别过小的问题,而是一个明确的 TLS 协议层失败事件。它不会因为日志级别调低就“变小”或被隐藏;相反,如果在错误日志里只看到这行字、没有上下文,恰恰说明日志级别可能太高(比如只记 error),导致关键诊断信息被过滤掉了。
真正需要优化的,是让这条错误带上足够多的上下文和底层线索,而不是让它“变小”。
? 看清方向:先确认是“谁连谁”失败
Nginx 日志中出现 SSL_do_handshake() failed,必须立刻看紧邻的上下文关键词:
出现
while SSL handshaking to upstream
→ 问题在 Nginx 主动连后端 HTTPS 服务时失败(Nginx 是客户端)
✅ 重点查:proxy_pass协议是否写错、proxy_ssl_server_name on是否启用、proxy_ssl_protocols是否兼容出现
while SSL handshaking, client: xxx.xxx.xxx.xxx
→ 问题在 外部客户端连 Nginx 时失败(Nginx 是服务端)
✅ 重点查:证书链是否完整、TLS 版本是否交集、密码套件是否匹配、OCSP stapling 是否异常
方向错了,所有后续排查都是白费。
? 日志增强:让错误“说话”
默认 error 级别日志太简略。要看到真正有用的线索,需确保以下配置生效:
-
开启详细 SSL 错误日志(Nginx 主配置):
error_log /var/log/nginx/error.log debug;
⚠️ 生产环境慎用
debug全局开启,可临时加在特定server或location块中,复现问题后关闭。 -
确保 OpenSSL 错误码输出可见:
SSL_do_handshake() failed后面通常紧跟着 OpenSSL 错误码,例如:-
error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure -
error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number error:141CF06C:SSL routines:tls_process_key_exchange:bad key share
这些错误码比文字描述更精准,直接对应协议层行为。
-
? 验证不靠猜:用 OpenSSL 快速定位根因
在 Nginx 服务器上执行(替换为你的域名和端口):
-
查协议与套件协商结果:
echo | openssl s_client -tls1_2 -connect example.com:443 -servername example.com 2>/dev/null | grep "Protocol\|Cipher" echo | openssl s_client -tls1_3 -connect example.com:443 -servername example.com 2>/dev/null | grep "Protocol\|Cipher"
-
查证书链是否下发完整:
echo | openssl s_client -connect example.com:443 -servername example.com -showcerts 2>/dev/null | openssl crl2pkcs7 -nocrl -out /dev/null 2>/dev/null && echo "证书链完整" || echo "证书链缺失"
-
检查是否因中间证书缺失导致无法验证:
echo | openssl s_client -connect example.com:443 -servername example.com -crl_check 2>&1 | grep "verify error\|revoked"
✅ 总结:所谓“优化”,其实是三件事
-
调高日志粒度:让
error_log ... debug;暴露 OpenSSL 错误码和握手细节 -
盯住上下文关键词:
to upstreamvsclient: xxx决定排查路径完全相反 - 用 OpenSSL 直连验证:绕过 Nginx 配置层,确认是协议、证书还是套件的真实瓶颈
不复杂,但容易忽略。


















