Nginx gRPC代理SSL握手失败需先看日志关键词:“to upstream”表明Nginx连后端失败,主因是grpc_pass协议(grpc://或grpcs://)与后端实际监听协议(HTTP/HTTPS)不匹配,须用curl/telnet直连验证并修正配置。

直接看错误日志里是否出现 “SSL_do_handshake() failed” 或 “connection refused” + “to upstream”,再结合后端监听协议确认——gRPC 代理若用 grpc_pass grpc:// 却连的是只支持 HTTPS 的后端,或反过来用 grpc_pass grpcs:// 去连 HTTP 端口,都会因协议不匹配被拒绝,根本不是网络不通,而是协议握手压根没开始。
查日志定位方向:先分清是 upstream 还是 client 问题
打开 Nginx error.log,搜索关键词:
- 含 “to upstream”(如 while SSL handshaking to upstream)→ 说明 Nginx 主动连后端失败,重点查 upstream 配置和后端协议支持
- 含 “client: xxx” 但无 upstream 字样 → 是客户端连 Nginx 失败,和后端 TLS 无关,不用往下排查
验证后端真实监听协议和端口
别猜,用 curl 和 telnet 直连后端地址,确认它到底支持什么:
- 如果 upstream 配的是
grpc://backend:50051(明文),就执行:curl -v http://backend:50051—— 应该能通(哪怕返回 404);curl -v https://backend:50051—— 应该失败或超时 - 如果配的是
grpcs://backend:50051(TLS),就执行:curl -v https://backend:50051—— 必须能完成 TLS 握手并返回响应头;telnet backend 50051—— 连上后敲几个字符,若立刻断开,大概率是 TLS 服务在等加密握手,不是端口关闭 - 特别注意:K8s 中写
127.0.0.1往往错——Nginx Pod 里的 localhost 不等于后端 Pod 的 localhost
检查 gRPC 代理指令与后端能力是否对齐
常见错配组合及修复方式:
-
grpc_pass grpc://+ 后端只监听 HTTPS(如用nginx -t检出后端实际跑在 443 且强制跳转)→ 改为grpcs://,并配证书或grpc_ssl_verify off(测试环境) -
grpc_pass grpcs://+ 后端是纯 HTTP(如本地开发的 Go gRPC server 默认不启 TLS)→ 改为grpc://,确保后端监听明文端口且支持 H2C - 混用协议:upstream 块里同时写了
server backend1:50051(HTTP)和server backend2:50052(HTTPS)→ 必须拆成两个独立 upstream,gRPC 不允许协议混用
抓包辅助确认连接是否真正发出
在 Nginx 机器上运行:
-
tcpdump -i any port 50051 -w grpc.pcap(替换成你的真实后端端口) - 触发一次 gRPC 调用,停止抓包,用 Wireshark 打开
- 若看到 SYN 发出但无 SYN-ACK 返回 → 后端没监听或防火墙拦截
- 若看到 SYN-ACK,接着是 Client Hello(TLS)但没 Server Hello → 后端开了端口,但不支持 TLS 或 SNI 错误
- 若看到 HTTP/2 PREFACE(
PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n)→ 说明走的是 H2C 明文,后端必须原生支持,不能靠反向代理二次转译


















