fix(geoip): send tunnel target in CONNECT Host header (js)

Node derives Host from the proxy hostname unless set explicitly; strict
backconnect proxies reject the mismatch, silently falling back to the
gateway's geo instead of the real exit IP.
This commit is contained in:
CloakHQ
2026-07-23 03:30:43 +02:00
parent 46a2b03b63
commit d640970d4a
+14 -10
View File
@@ -261,20 +261,24 @@ async function resolveExitIp(proxyUrl: string | null | undefined, timeoutMs?: nu
try {
const ip = await new Promise<string | null>((resolve, reject) => {
const targetUrl = new URL(echoUrl);
// Host must name the tunnel target, not the proxy (RFC 9110 §7.2).
// Node derives Host from `host` (the proxy) unless set explicitly;
// strict proxies (e.g. Oxylabs backconnect) reject the mismatch.
const authority = `${targetUrl.hostname}:443`;
const headers: Record<string, string> = { Host: authority };
if (proxyUrlObj.username) {
headers["Proxy-Authorization"] =
"Basic " +
Buffer.from(
`${decodeURIComponent(proxyUrlObj.username)}:${decodeURIComponent(proxyUrlObj.password || "")}`
).toString("base64");
}
const connectReq = http.request({
host: proxyUrlObj.hostname,
port: parseInt(proxyUrlObj.port || "80", 10),
method: "CONNECT",
path: `${targetUrl.hostname}:443`,
headers: proxyUrlObj.username
? {
"Proxy-Authorization":
"Basic " +
Buffer.from(
`${decodeURIComponent(proxyUrlObj.username)}:${decodeURIComponent(proxyUrlObj.password || "")}`
).toString("base64"),
}
: {},
path: authority,
headers,
timeout: Math.min(10_000, remaining ?? 10_000),
});