Nginx反向代理旧Java微服务SSL握手失败,主因是proxy_ssl_protocols未与后端实际支持的TLS版本对齐;必须通过openssl s_client实测确认(如-tls1/-tls1_1),并在location中精准配置proxy_ssl_protocols TLSv1 TLSv1.1等,同步调整proxy_ssl_ciphers、proxy_ssl_server_name等配套项。

Nginx 作为反向代理连接旧版 Java 微服务(如基于 JDK 7u95、JDK 8u31 前或 Spring Boot 1.x 内嵌 Tomcat 的服务)时,SSL_do_handshake() failed 或 ssl handshake failed 错误大概率是 proxy_ssl_protocols 配置不匹配导致的——它不是“要不要降级”,而是“必须对齐”。
确认微服务实际支持的 TLS 版本
别猜,直接测。Java 旧版本默认不启用 TLSv1.2,但是否禁用、是否被启动参数硬锁,得靠实证:
- 用
openssl s_client -connect your-service:8443 -tls1测试 TLSv1:若返回证书且不报错,说明支持 - 再试
openssl s_client -connect your-service:8443 -tls1_1:成功则支持 TLSv1.1;失败但上一条成功,说明只认 TLSv1 - 若
-tls1_2报no protocols available或握手直接中断,基本可断定后端不支持 TLSv1.2+ - 检查 Java 启动参数:是否存在
-Dhttps.protocols=TLSv1或-Djdk.tls.client.protocols=TLSv1—— 这会强制覆盖 JVM 默认行为
在 proxy_pass 作用域内设置 proxy_ssl_protocols
proxy_ssl_protocols 控制 Nginx **作为客户端**去连上游时用什么 TLS 版本,它和面向用户的 ssl_protocols 完全无关,不能写在 http 或 server 块顶层。
- 若微服务只支持 TLSv1:
proxy_ssl_protocols TLSv1; - 若支持 TLSv1 和 TLSv1.1(常见于 JDK 8u60 前):
proxy_ssl_protocols TLSv1 TLSv1.1; - 绝对不要加
TLSv1.2或更高版本,否则 Nginx 会主动拒绝协商低版本,握手必挂 - Nginx 1.19.4+ 默认已移除 TLSv1 支持,需确保编译时 OpenSSL ≥ 1.0.1 且未禁用旧协议;CentOS 7 的 OpenSSL 1.0.2k 通常可用
配套必须同步调整的三项配置
光调 proxy_ssl_protocols 不够,旧 Java 对加密套件、SNI、证书校验都更敏感:
- 加密套件要退兼容:
proxy_ssl_ciphers "ECDHE-RSA-AES128-SHA:AES128-SHA";(避免 AES-GCM、CHACHA20 等 TLSv1.2+ 才支持的 AEAD 套件) - 开启 SNI(多数情况需要):
proxy_ssl_server_name on;,否则多域名共用 IP 的微服务可能返回错误证书 - 证书校验谨慎关闭:
proxy_ssl_verify off;仅限内网可信环境临时使用;若必须关,请配proxy_ssl_trusted_certificate指向内部 CA 根证书
验证与上线前关键检查点
改完别急 reload,先做三件事:
- 用
nginx -t检查语法,特别注意proxy_ssl_protocols是否被放在了upstream或location内,而非全局块 - 在测试环境用
curl -v https://nginx-proxy/ --resolve 'your-service:8443:127.0.0.1'观察 TLS 协议协商结果(看* ALPN, offering h2下面有没有* SSL connection using TLSv1.1 / AES128-SHA) - 抓包确认:用
tcpdump -i lo port 8443 -w tls.pcap+ Wireshark 查看 ClientHello 中 advertised versions 是否含你指定的版本
最易被忽略的是:Nginx 日志里不会明说“用了 TLSv1”,但 error_log ... debug 下能看到 SSL_do_handshake() failed (SSL: error:141E70BF:SSL routines:tls_construct_client_hello:no protocols available) 这类线索——它其实已经告诉你,Nginx 尝试的协议列表里没一个被后端接受。


















