{"record":{"id":"2944be8007e4eb77","repo":"sveltejs/kit","slug":"cannot-redirect-to-json-stringify-location-ur","errorCode":null,"errorMessage":"Cannot redirect to ${JSON.stringify(location)}: URL origin is not included in the `external` allowlist (prod: 'Cannot redirect to external URL unless explicitly allowed')","messagePattern":"Cannot redirect to (.+?): URL origin is not included in the `external` allowlist \\(prod: 'Cannot redirect to external URL unless explicitly allowed'\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/kit/src/exports/url.js","lineNumber":71,"sourceCode":"\t\t);\n\t}\n\n\tif (external === true) {\n\t\tif (is_javascript_location(location)) {\n\t\t\tthrow new Error(\n\t\t\t\tDEV\n\t\t\t\t\t? `Cannot redirect to ${JSON.stringify(location)} with \\`{ external: true }\\`. ` +\n\t\t\t\t\t\t\t'The `javascript:` and `data:` protocols must be explicitly listed in the `external` allowlist'\n\t\t\t\t\t: 'Cannot redirect to external URL unless explicitly allowed'\n\t\t\t);\n\t\t}\n\n\t\treturn;\n\t}\n\n\tif (Array.isArray(external)) {\n\t\tif (!external.some((allowed) => matches_external_allowlist_entry(location, allowed))) {\n\t\t\tthrow new Error(\n\t\t\t\tDEV\n\t\t\t\t\t? `Cannot redirect to ${JSON.stringify(location)}: URL origin is not included in the \\`external\\` allowlist`\n\t\t\t\t\t: 'Cannot redirect to external URL unless explicitly allowed'\n\t\t\t);\n\t\t}\n\n\t\treturn;\n\t}\n\n\tthrow new Error(\n\t\tDEV\n\t\t\t? '`redirect` options.external must be `true` or an array of allowed origins'\n\t\t\t: 'Invalid redirect options.external value'\n\t);\n}\n","sourceCodeStart":53,"sourceCodeEnd":87,"githubUrl":"https://github.com/sveltejs/kit/blob/03f1687fe612ce3d2d9131139b5b188d9cf90c64/packages/kit/src/exports/url.js#L53-L87","documentation":"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.","triggerScenarios":"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`).","commonSituations":"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.","solutions":["Add the target origin to the `external` allowlist array in the redirect call","Redirect with a relative URL if the target is on the same origin","Pass `external: true` if any external URL is intentionally allowed","Verify the URL being built at runtime — the origin may come from user input or an env var you didn't expect"],"exampleFix":"// before\nredirect(302, `https://accounts.google.com/o/oauth2/auth?...`, { external: ['https://auth.example.com'] });\n// after\nredirect(302, `https://accounts.google.com/o/oauth2/auth?...`, { external: ['https://auth.example.com', 'https://accounts.google.com'] });","handlingStrategy":"validation","validationCode":"const url = new URL(location, event.url.origin);\nconst allowed = ['https://auth.example.com', 'https://accounts.google.com'];\nif (url.origin !== event.url.origin && !allowed.includes(url.origin)) {\n  throw new Error(`Refusing redirect to non-allowlisted origin: ${url.origin}`);\n}","typeGuard":"function isAllowedExternal(location, allowlist) {\n  try {\n    const url = new URL(location);\n    return allowlist.some((a) => new URL(a).origin === url.origin);\n  } catch {\n    return false;\n  }\n}","tryCatchPattern":"try {\n  redirect(302, target, { external: allowlist });\n} catch (err) {\n  if (/external allowlist/.test(err.message)) {\n    throw redirect(302, '/fallback');\n  }\n  throw err;\n}","preventionTips":["Keep the external allowlist in one shared config module used by every redirect","Never build redirect targets from raw user input without validating the origin","Prefer relative redirects for same-origin navigation","Log rejected redirect attempts to catch allowlist gaps early"],"tags":["redirect","security","open-redirect","allowlist"],"backgroundTag":"redirect-origin-not-allowed","analyzedSha":"03f1687fe612ce3d2d9131139b5b188d9cf90c64","analyzedAt":"2026-09-02T02:01:50.504Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}