排查nginx https握手协议协商故障,核心是先通过error.log中“to upstream”或“client: xxx”锁定协商方向(nginx作为客户端或服务端),再依据openssl错误码(如error:14094410、error:14209102等)精准定位sni缺失、tls版本不兼容或套件无交集等问题,最后实测验证两端协议与套件支持情况并针对性调整配置。

排查 Nginx HTTPS 握手过程中的加密协议协商故障,核心是抓住“谁在跟谁协商”和“卡在哪一步”,而不是盲目调证书或重配 TLS 版本。日志里的关键词和 OpenSSL 错误码,比配置文件本身更直接、更可信。
先看 error.log,锁定协商方向
打开 /var/log/nginx/error.log,找含 SSL_do_handshake() failed 的行,重点看它前后的上下文:
- 出现
while SSL handshaking to upstream→ Nginx 是客户端,问题出在proxy_pass https://连后端时,比如 SNI 没开、会话复用错乱、后端根本没启 HTTPS - 出现
while SSL handshaking, client: xxx.xxx.xxx.xxx→ Nginx 是服务端,问题出在浏览器/App 连你这台服务器时,比如证书链不全、TLS 版本太新、密码套件无交集
90% 的线上握手失败属于前者——Nginx 代理连后端失败,但很多人一上来就查自己域名的证书,方向错了,所有操作都白费。
查 OpenSSL 错误码,直击协议层
错误日志里紧跟的十六进制码(如 error:14094410)才是关键线索,比文字描述更准:
-
error:1408F10B(wrong version number)→ 后端监听的是 HTTP,你却用https://去连;或客户端把 HTTPS 请求发到了 HTTP 端口 -
error:14094410(handshake failure)→ 大概率缺 SNI(proxy_ssl_server_name on没开),或两端密码套件完全没交集 -
error:14209102(unsupported protocol)→ TLS 版本不兼容,比如 Nginx 只允许 TLSv1.2,但后端只支持 TLSv1.3 -
error:141CF06C(bad key share)→ TLS 1.3 密钥交换失败,常见于后端 OpenSSL 或 JDK 版本太老(如 JDK 8u2XX 以下) -
error:1417A0C1(no shared cipher)→ 加密套件无交集,不是证书问题,是“语言不通”
验证两端实际支持的协议与套件
别只看配置,要实测:
- 查后端是否真能跑 HTTPS:
curl -v https://backend-domain:port,看是否出现* SSL connection using …和* Server certificate - 模拟客户端能力:
openssl s_client -connect your-domain.com:443 -tls1_2 -cipher 'ECDHE-RSA-AES128-SHA',逐个试常用旧套件,确认能否连通 - 查证书链完整性:
openssl s_client -connect your-domain.com:443 -showcerts,确认返回的不只是叶子证书,还有中间 CA - 检查 Nginx 实际启用的套件:
openssl ciphers -V "YOUR_SSL_CIPHERS",确保写进配置的套件被当前 OpenSSL 版本真正支持
针对性调整关键配置项
根据定位结果,改对应项,不搞“全量宽松”:
- 如果是
to upstream类失败:加proxy_ssl_server_name on;,关proxy_ssl_session_reuse off;,限定proxy_ssl_protocols TLSv1.2 TLSv1.3; - 如果是客户端连 Nginx 失败:补全证书链(含中间 CA),保留 PFS 套件(如
ECDHE-RSA-AES128-SHA256),兼容老设备可加ECDHE-RSA-AES128-SHA,但禁用!MD5 !RC4 !EXPORT - 若怀疑握手超时:设
ssl_handshake_timeout 6s;(公网推荐值),避免卡住 worker 进程











