2
0
mirror of https://github.com/tenrok/axios.git synced 2026-06-17 19:21:29 +03:00

fix(http): preserve user-supplied Host header when forwarding through a proxy (#10805) (#10822)

* fix(http): preserve user-supplied Host header when forwarding through a proxy (#10805)

When sending a request through a proxy with the http adapter, axios was
unconditionally rewriting `options.headers.host` to the request URL's
hostname:port, overwriting any Host header the caller had set in
`config.headers`.

This made it impossible to direct a proxy to a virtual host that differs
from the request URL — for example, hitting `127.0.0.1:4000` while
asking the proxy to treat the request as `example.com`.

Skip the default assignment when a Host header is already present
(case-insensitive match, since users may pass `host`, `Host`, or `HOST`).

* chore: apply some nitpicks

---------

Co-authored-by: Jason Saayman <jasonsaayman@gmail.com>
This commit is contained in:
Ashish kumar choubey
2026-04-30 22:03:33 +05:30
committed by GitHub
parent 79f39e1d04
commit 0e8b6bbb54
3 changed files with 76 additions and 1 deletions
+6
View File
@@ -928,6 +928,12 @@ These are the available config options for making requests. Only the `url` is re
// This will set a `Proxy-Authorization` header, overwriting any existing
// `Proxy-Authorization` custom headers you have set using `headers`.
// If the proxy server uses HTTPS, then you must set the protocol to `https`.
// A user-supplied `Host` header in `headers` is preserved when forwarding
// through a proxy (case-insensitive match on `host`/`Host`/`HOST`); this
// lets you target a virtual host that differs from the request URL — for
// example, hitting `127.0.0.1:4000` while having the proxy treat the
// request as `example.com`. If no `Host` header is supplied, axios
// defaults it to the request URL's `hostname:port` as before.
proxy: {
protocol: 'https',
host: '127.0.0.1',
+12 -1
View File
@@ -236,7 +236,18 @@ function setProxy(options, configProxy, location, isRedirect) {
options.headers['Proxy-Authorization'] = 'Basic ' + base64;
}
options.headers.host = options.hostname + (options.port ? ':' + options.port : '');
// Preserve a user-supplied Host header (case-insensitive) so callers can override
// the value forwarded to the proxy; otherwise default to the request URL's host.
let hasUserHostHeader = false;
for (const name in options.headers) {
if (name.toLowerCase() === 'host') {
hasUserHostHeader = true;
break;
}
}
if (!hasUserHostHeader) {
options.headers.host = options.hostname + (options.port ? ':' + options.port : '');
}
const proxyHost = proxy.hostname || proxy.host;
options.hostname = proxyHost;
// Replace 'host' since options is not a URL object
+58
View File
@@ -2371,6 +2371,64 @@ describe('supports http with nodejs', () => {
}
});
describe('Host header preservation when forwarding through a proxy (#10805)', () => {
const proxyConfig = { hostname: '127.0.0.1', protocol: 'http:', port: 8888 };
it('defaults the Host header to the request target when the user does not set one', () => {
const options = {
headers: {},
beforeRedirects: {},
hostname: '127.0.0.1',
port: 4000,
};
__setProxy(options, proxyConfig, 'http://127.0.0.1:4000/');
assert.strictEqual(options.headers.host, '127.0.0.1:4000');
});
it('preserves a user-supplied lowercase host header', () => {
const options = {
headers: { host: 'example.com' },
beforeRedirects: {},
hostname: '127.0.0.1',
port: 4000,
};
__setProxy(options, proxyConfig, 'http://127.0.0.1:4000/');
assert.strictEqual(options.headers.host, 'example.com');
});
it('preserves a user-supplied Host header regardless of casing', () => {
const options = {
headers: { Host: 'example.com' },
beforeRedirects: {},
hostname: '127.0.0.1',
port: 4000,
};
__setProxy(options, proxyConfig, 'http://127.0.0.1:4000/');
assert.strictEqual(options.headers.Host, 'example.com');
assert.strictEqual(options.headers.host, undefined);
});
it('preserves a user-supplied Host header across a redirect re-invocation', () => {
const options = {
headers: { Host: 'example.com' },
beforeRedirects: {},
hostname: '127.0.0.1',
port: 4000,
};
__setProxy(options, proxyConfig, 'http://127.0.0.1:4000/', true);
assert.strictEqual(options.headers.Host, 'example.com');
assert.strictEqual(options.headers.host, undefined);
});
});
describe('Proxy-Authorization header leak on redirect (GHSA-j5f8-grm9-p9fc)', () => {
it('clears a stale Proxy-Authorization header when redirected request resolves to no proxy (configProxy=false)', () => {
const options = {