错误信息
Client network socket disconnected before secure TLS connection was established
也可能输出为
- code: 'ECONNRESET'
使用 fetch() 时,它是“TypeError: fetch failed”的原因;npm 和 yarn 会把它打印在各自的“request to … failed”消息中。同样的文字配上不同的代码(例如 ETIMEDOUT),指向的是超时而不是连接重置。
Node.js tls / https / fetch — code ECONNRESET during the TLS handshake
含义
Node.js 发送 ClientHello 之后、TLS 握手完成之前,对方(或中间的某个设备)关闭了 TCP 连接。由于没有收到 TLS 告警,Node.js 无法说明原因;它报告的是连接重置,代码为 ECONNRESET。原因在服务器、代理或防火墙上,而不在证书上。
原因
网络要求使用公司代理,但没有使用(或配置错误)
常见只允许通过代理发出 HTTPS 请求的网络,往往会让 TCP 连接成功,然后中断握手。Node.js 默认不读取 HTTPS_PROXY,因此在浏览器中正常工作的工具在这里会失败。
防火墙或 TLS 过滤器中断了握手
常见下一代防火墙按 SNI 或 TLS 指纹拦截,直接关闭连接;VPN 客户端和 DNS 过滤也可能这样做。
服务器关闭了它无法处理的握手
偶见在缺少 SNI、名称未知或没有匹配的协议版本时,一些服务器和负载均衡器会直接关闭连接,而不是发送告警。
过载、限流或连接数限制
偶见达到连接数上限的服务器或上游限流器会重置新连接;这时错误会在负载较高时间歇出现。
修复方法
- 1.
检查代理设置
把环境中的代理配置与浏览器使用的配置(通常是 PAC 文件)对比。代理 URL 通常应为 http://,内部主机应放进 NO_PROXY。
bash env | grep -i -E '^(https?|no)_proxy=' npm config get proxy; npm config get https-proxy # Proxy URL with http:// and internal hosts in NO_PROXY export HTTPS_PROXY=http://proxy.example.net:3128 NO_PROXY=localhost,127.0.0.1,.internal.example.com - 2.
用 openssl s_client 复现握手
如果 s_client 也被断开,问题就在网络或服务器上;变换 SNI 的设置,看服务器是否需要它。
bash # Same handshake without Node.js: does the server or network cut it too? openssl s_client -connect api.example.com:443 -servername api.example.com -brief </dev/null openssl s_client -connect api.example.com:443 -noservername -brief </dev/null - 3.
让 Node.js 使用代理环境变量
设置 NODE_USE_ENV_PROXY=1 后,较新的 Node.js 版本会对 http、https 和 fetch 遵循 HTTP_PROXY、HTTPS_PROXY 和 NO_PROXY;axios、got 或 undici 的 ProxyAgent 等库有各自的代理选项。
bash # Node.js does not use HTTPS_PROXY by itself; recent releases can be told to NODE_USE_ENV_PROXY=1 HTTPS_PROXY=http://proxy.example.net:3128 node app.js
相关错误
[SSL: UNEXPECTED_EOF_WHILE_READING] EOF occurred in violation of protocolPython ssl module (OpenSSL)code: 'ERR_SSL_WRONG_VERSION_NUMBER'Node.js tls.connect / fetch (undici)javax.net.ssl.SSLHandshakeException: Remote host terminated the handshakeJava (JSSE)error:0A000126:SSL routines::unexpected eof while readingOpenSSL 3