Skip to content

Default headers from an extended client are still sent when calling it with an absolute URL #2461

Description

@Str1ckl4nd

Describe the bug

  • Node.js version: not executed locally; static check used Got v15.0.7, which declares Node.js >=22
  • OS & version: not OS-specific

Got's documentation describes prefixUrl as "prepended to url", and the quick start says paths must be relative to the prefix. The options documentation also notes that absolute URLs require prefixUrl: ''.

That explains why an absolute string URL is not prefixed. The surprising part is that when an extended instance has both prefixUrl and default headers, calling the instance with an absolute string URL skips the prefix but still sends the inherited headers to that absolute URL.

That makes an extended instance behave less like an origin-specific API client than the prefixUrl examples suggest.

Actual behavior

Given an instance like this:

const client = got.extend({
	prefixUrl: 'http://127.0.0.1:3000',
	headers: {
		authorization: 'Bearer demo-token',
	},
});

A relative path is sent to the prefixed API origin:

await client('collect');

Calling the same instance with an absolute string URL:

await client('http://127.0.0.1:4000/collect');

sends the request to 127.0.0.1:4000, and the inherited authorization header is still present on that request.

Expected behavior

For an instance configured with both prefixUrl and default headers, I would expect inherited origin-specific headers not to be sent when the request URL is an absolute URL with a different origin from prefixUrl.

If the current behavior is intended, it would help to document explicitly that absolute string URLs skip prefixUrl while still inheriting instance headers/options.

Code to reproduce

import http from 'node:http';
import got from 'got';

const listen = async server => new Promise(resolve => {
	server.listen(0, '127.0.0.1', () => {
		const {port} = server.address();
		resolve(`http://127.0.0.1:${port}`);
	});
});

const apiServer = http.createServer((_request, response) => {
	response.end('api server');
});

const otherServer = http.createServer((request, response) => {
	console.log('received by other origin:', {
		url: request.url,
		authorization: request.headers.authorization,
	});
	response.end('other server');
});

const apiUrl = await listen(apiServer);
const otherUrl = await listen(otherServer);

const client = got.extend({
	prefixUrl: apiUrl,
	headers: {
		authorization: 'Bearer demo-token',
	},
});

await client('collect');
await client(`${otherUrl}/collect`);

apiServer.close();
otherServer.close();

Observed output:

received by other origin: {
  url: '/collect',
  authorization: 'Bearer demo-token'
}

Checklist

  • I have read the documentation.
  • I have tried my code with the latest version of Node.js and Got.

Static version check: the current GitHub tag line ends at v15.0.7, and the checked source is 66ca4e025622e1dd71f4e4f13aa6e1b79d61b069 (v15.0.7). The same behavior was previously observed on v15.0.6; I can provide more environment details if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions