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) 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:
committed by
GitHub
parent
79f39e1d04
commit
0e8b6bbb54
@@ -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
@@ -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
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
Reference in New Issue
Block a user