sindresorhus/got · error · Error

The `url` option must stay on the same origin as `prefixUrl`

Error message

The `url` option must stay on the same origin as `prefixUrl` when `allowAbsoluteUrls` is false

What it means

Thrown at source/core/options.ts:1070 by `assertUrlHasSameOriginAsPrefixUrlIfNeeded`. Even when the per-call URL is technically relative and resolves under prefixUrl, if the resolved origin differs from the prefixUrl origin AND allowAbsoluteUrls is false, got throws. This catches cross-origin hops that arise from prefixUrl/path resolution (e.g. prefixUrl on `https://a.com` resolving a path that flips origin via redirect or option overrides) — the library treats origin changes as security-relevant because they may leak credentials or headers across trust boundaries.

Source

Thrown at source/core/options.ts:1070

export const assertUrlHasSameOriginAsPrefixUrlIfNeeded = (options: Options, url: URL): void => {
	if (!options.prefixUrl || options.allowAbsoluteUrls) {
		return;
	}

	let prefixUrl: URL;

	try {
		prefixUrl = new URL(options.prefixUrl);
	} catch {
		return;
	}

	if (isSameOrigin(prefixUrl, url)) {
		return;
	}

	throw new Error('The `url` option must stay on the same origin as `prefixUrl` when `allowAbsoluteUrls` is false');
};

export type UrlPrefixBoundary = {
	url?: URL;
	prefixUrl?: string;
	allowAbsoluteUrls?: boolean;
};

export const getUrlPrefixBoundary = (options: Options): UrlPrefixBoundary => ({
	url: options.url instanceof URL ? new URL(options.url) : undefined,
	prefixUrl: options.prefixUrl.toString(),
	allowAbsoluteUrls: options.allowAbsoluteUrls,
});

export const hasUrlOrPrefixUrlBoundaryChanged = (options: Options, currentUrl: URL, previous: UrlPrefixBoundary): boolean => (
	currentUrl.href !== previous.url?.href
	|| options.prefixUrl.toString() !== previous.prefixUrl
	|| options.allowAbsoluteUrls !== previous.allowAbsoluteUrls

View on GitHub (pinned to e3924aa1e5)

Solutions

  1. Confirm the intended target origin matches prefixUrl, or set `allowAbsoluteUrls: true` if cross-origin calls are intentional.
  2. In retry/afterResponse hooks that change origin, use a separate got instance configured for that origin instead of mutating options.url.
  3. Verify prefixUrl spelling/protocol/host — a stray `http://` vs `https://` or `www.` vs bare domain will trigger this.

Example fix

// before
const client = got.extend({ prefixUrl: 'https://api.example.com' });
hooks: { afterResponse: [(r, retry) => retry({ url: 'https://api.other.com/path' })] }

// after — separate instance for the other origin
const client = got.extend({ prefixUrl: 'https://api.example.com' });
const other = got.extend({ prefixUrl: 'https://api.other.com' });
hooks: { afterResponse: [(r) => r.statusCode === 308 ? other('path') : r] }
Defensive patterns

Strategy: validation

Validate before calling

function assertSameOrigin(prefixUrl, targetUrl) {
  if (!prefixUrl) return;
  const a = new URL(prefixUrl);
  const b = targetUrl instanceof URL ? targetUrl : new URL(targetUrl, prefixUrl);
  if (a.origin !== b.origin) {
    throw new Error(`Cross-origin jump from ${a.origin} to ${b.origin} blocked by allowAbsoluteUrls=false`);
  }
}
assertSameOrigin(options.prefixUrl, options.url);

Type guard

function isSameOrigin(a: string | URL, b: string | URL): boolean {
  const ua = a instanceof URL ? a : new URL(a);
  const ub = b instanceof URL ? b : new URL(b);
  return ua.origin === ub.origin;
}

Try / catch

try {
  await client(url, options);
} catch (error) {
  if (error instanceof Error && /must stay on the same origin as `prefixUrl`/.test(error.message)) {
    // route cross-origin calls through a dedicated instance
    const crossOriginClient = got.extend({ prefixUrl: new URL(url).origin, allowAbsoluteUrls: true });
    return crossOriginClient(new URL(url).pathname);
  }
  throw error;
}

Prevention

When it happens

Trigger: Calling applyUrlOverride / afterResponse retry with a URL whose origin differs from prefixUrl; prefixUrl set to `https://a.example.com` and a retry/hook switches to `https://b.example.com`; an option merge that changes the resolved URL to a different origin while allowAbsoluteUrls is false.

Common situations: Multi-tenant API clients where a redirect moves between subdomains; SSO flows that hop from app origin to IdP origin; retry hooks that switch to a fallback host; misconfigured prefixUrl with a typo'd host.

Related errors


AI-assisted analysis of sindresorhus/got@e3924aa1e5 (2026-08-03). Data as JSON: /data/errors/691de7d78943990b.json. Report an issue: GitHub.