From 5d37cfa75d61dae03f72a590169bda07fb365f36 Mon Sep 17 00:00:00 2001 From: lazerg Date: Wed, 14 Oct 2026 13:28:24 +0500 Subject: [PATCH] http: send SNI when connecting to https proxies When the proxy URL uses https://, the agents connected to the proxy with tls.connect() using only the host and port, so no SNI was sent and proxies that pick their certificate by SNI could answer with a certificate that fails verification. Set servername to the proxy host when it is not an IP address, matching undici. Signed-off-by: lazerg Assisted-by: Claude Code --- lib/internal/http.js | 6 +++++- test/client-proxy/test-http-proxy-request-https-proxy.mjs | 3 +++ test/client-proxy/test-https-proxy-request-https-proxy.mjs | 3 +++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/internal/http.js b/lib/internal/http.js index 999ade29ed07..de86bde5265e 100644 --- a/lib/internal/http.js +++ b/lib/internal/http.js @@ -19,7 +19,7 @@ const { const { URL } = require('internal/url'); const { Buffer } = require('buffer'); -const { isIPv4 } = require('internal/net'); +const { isIP, isIPv4 } = require('internal/net'); const { ERR_PROXY_INVALID_CONFIG } = require('internal/errors').codes; let utcCache; let utcHeaderCache; @@ -149,6 +149,10 @@ class ProxyConfig { // The port comes from parsed URL so it is either '' or a valid number string. port: port ? Number(port) : (protocol === 'https:' ? 443 : 80), }; + // Send SNI to HTTPS proxies, but not for IP addresses, which are not valid SNI values. + if (protocol === 'https:' && !isIP(this.proxyConnectionOptions.host)) { + this.proxyConnectionOptions.servername = this.proxyConnectionOptions.host; + } } // See: https://about.gitlab.com/blog/we-need-to-talk-no-proxy diff --git a/test/client-proxy/test-http-proxy-request-https-proxy.mjs b/test/client-proxy/test-http-proxy-request-https-proxy.mjs index 685936fc3ac0..85e9c6c0c69b 100644 --- a/test/client-proxy/test-http-proxy-request-https-proxy.mjs +++ b/test/client-proxy/test-http-proxy-request-https-proxy.mjs @@ -23,6 +23,9 @@ await once(server, 'listening'); // Start an HTTPS proxy server. const { proxy, logs } = createProxyServer({ https: true }); +proxy.on('secureConnection', common.mustCall((socket) => { + assert.strictEqual(socket.servername, 'localhost'); +})); proxy.listen(0); await once(proxy, 'listening'); diff --git a/test/client-proxy/test-https-proxy-request-https-proxy.mjs b/test/client-proxy/test-https-proxy-request-https-proxy.mjs index bb0e8df0ea96..b6a8439de39f 100644 --- a/test/client-proxy/test-https-proxy-request-https-proxy.mjs +++ b/test/client-proxy/test-https-proxy-request-https-proxy.mjs @@ -29,6 +29,9 @@ await once(server, 'listening'); // Start an HTTPS proxy server. const { proxy, logs } = createProxyServer({ https: true }); +proxy.on('secureConnection', common.mustCall((socket) => { + assert.strictEqual(socket.servername, 'localhost'); +})); proxy.listen(0); await once(proxy, 'listening');