sveltejs/kit · error · Error

Cannot redirect to ${JSON.stringify(location)}: URL origin i

Error message

Cannot redirect to ${JSON.stringify(location)}: URL origin is not included in the `external` allowlist (prod: 'Cannot redirect to external URL unless explicitly allowed')

What it means

SvelteKit's `redirect()` only allows redirecting to absolute external URLs when the `external` option explicitly allows them. When `external` is an array, each target origin is matched against the allowlist entries; a URL whose origin matches no entry throws this error (a generic message in production). This prevents open-redirect vulnerabilities by default.

Source

Thrown at packages/kit/src/exports/url.js:71

		);
	}

	if (external === true) {
		if (is_javascript_location(location)) {
			throw new Error(
				DEV
					? `Cannot redirect to ${JSON.stringify(location)} with \`{ external: true }\`. ` +
							'The `javascript:` and `data:` protocols must be explicitly listed in the `external` allowlist'
					: 'Cannot redirect to external URL unless explicitly allowed'
			);
		}

		return;
	}

	if (Array.isArray(external)) {
		if (!external.some((allowed) => matches_external_allowlist_entry(location, allowed))) {
			throw new Error(
				DEV
					? `Cannot redirect to ${JSON.stringify(location)}: URL origin is not included in the \`external\` allowlist`
					: 'Cannot redirect to external URL unless explicitly allowed'
			);
		}

		return;
	}

	throw new Error(
		DEV
			? '`redirect` options.external must be `true` or an array of allowed origins'
			: 'Invalid redirect options.external value'
	);
}

View on GitHub (pinned to 03f1687fe6)

Solutions

  1. Add the target origin to the `external` allowlist array in the redirect call
  2. Redirect with a relative URL if the target is on the same origin
  3. Pass `external: true` if any external URL is intentionally allowed
  4. Verify the URL being built at runtime — the origin may come from user input or an env var you didn't expect

Example fix

// before
redirect(302, `https://accounts.google.com/o/oauth2/auth?...`, { external: ['https://auth.example.com'] });
// after
redirect(302, `https://accounts.google.com/o/oauth2/auth?...`, { external: ['https://auth.example.com', 'https://accounts.google.com'] });
Defensive patterns

Strategy: validation

Validate before calling

const url = new URL(location, event.url.origin);
const allowed = ['https://auth.example.com', 'https://accounts.google.com'];
if (url.origin !== event.url.origin && !allowed.includes(url.origin)) {
  throw new Error(`Refusing redirect to non-allowlisted origin: ${url.origin}`);
}

Type guard

function isAllowedExternal(location, allowlist) {
  try {
    const url = new URL(location);
    return allowlist.some((a) => new URL(a).origin === url.origin);
  } catch {
    return false;
  }
}

Try / catch

try {
  redirect(302, target, { external: allowlist });
} catch (err) {
  if (/external allowlist/.test(err.message)) {
    throw redirect(302, '/fallback');
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling `redirect(302, 'https://evil.com/path', { external: ['https://good.com'] })` or any absolute URL to an origin not listed in the `external` allowlist array passed to `redirect` (or the underlying `validate_redirect_location`).

Common situations: Redirecting to OAuth/IDP callbacks, payment providers, or another subdomain after login; an allowlist that was written for one environment (staging origin) but the code now redirects to a production origin.

Related errors


AI-assisted analysis of sveltejs/kit@03f1687fe6 (2026-09-02). Data as JSON: /api/errors/2944be8007e4eb77. Report an issue: GitHub.