{"record":{"id":"82c233b5d40e0849","repo":"withastro/astro","slug":"unsupportedexternalredirect","errorCode":"UnsupportedExternalRedirect","errorMessage":"The destination URL in the external redirect from \"${from}\" to \"${to}\" is unsupported.","messagePattern":"The destination URL in the external redirect from \"(.+?)\" to \"(.+?)\" is unsupported\\.","errorType":"exception","errorClass":"AstroError","httpStatus":null,"severity":"error","filePath":"packages/astro/src/core/routing/create-manifest.ts","lineNumber":542,"sourceCode":"\t\tconst pathname = segments.every((segment) => segment.length === 1 && !segment[0].dynamic)\n\t\t\t? `/${segments.map((segment) => segment[0].content).join('/')}`\n\t\t\t: null;\n\t\tconst params = segments\n\t\t\t.flat()\n\t\t\t.filter((p) => p.dynamic)\n\t\t\t.map((p) => p.content);\n\t\tconst route = joinSegments(segments);\n\n\t\tlet destination: string;\n\t\tif (typeof to === 'string') {\n\t\t\tdestination = to;\n\t\t} else {\n\t\t\tdestination = to.destination;\n\t\t}\n\n\t\t// check if the link starts with http or https; if not, throw an error\n\t\tif (URL.canParse(destination) && !/^https?:\\/\\//.test(destination)) {\n\t\t\tthrow new AstroError({\n\t\t\t\t...UnsupportedExternalRedirect,\n\t\t\t\tmessage: UnsupportedExternalRedirect.message(from, destination),\n\t\t\t});\n\t\t}\n\n\t\tconst redirectRoute = routeMap.get(destination);\n\n\t\t// If the source has dynamic params and the redirect will be prerendered,\n\t\t// we need a valid redirectRoute to map them. Without it, the build will fail\n\t\t// later with a misleading error. Catch this early and provide a clear error message.\n\t\tif (\n\t\t\tparams.length > 0 &&\n\t\t\t!redirectRoute &&\n\t\t\t!URL.canParse(destination) &&\n\t\t\tgetPrerenderDefault(config)\n\t\t) {\n\t\t\tthrow new AstroError({\n\t\t\t\t...InvalidRedirectDestination,","sourceCodeStart":524,"sourceCodeEnd":560,"githubUrl":"https://github.com/withastro/astro/blob/e294953aa8aadd98d5be92e60a03037b05dbdfd4/packages/astro/src/core/routing/create-manifest.ts#L524-L560","documentation":"In the `redirects` config, a destination that parses as a URL is treated as external and must use the `http` or `https` scheme — Astro writes it into the generated redirect as-is. If `URL.canParse(destination)` succeeds but the string does not match `/^https?:\\/\\//` (e.g. `mailto:`, `ftp://`, `file://`, custom schemes), manifest creation throws `UnsupportedExternalRedirect`. Strings that do not parse as URLs fall through to internal route matching instead.","triggerScenarios":"`redirects: { '/contact': 'mailto:support@example.com' }`; redirecting to `ftp://…`, `file://…`, or an app-deep-link scheme (`myapp://…`); config values pulled from a CMS where the scheme is user-controlled.","commonSituations":"Trying to redirect to email/chat deep links; migrating server-side redirect maps that contained non-HTTP schemes; copy-pasting redirect tables from nginx/Netlify configs that allowed broader schemes.","solutions":["Point the redirect at an `http(s)://` URL only","For mailto/app links, don't redirect — link directly, or land on a page that offers the link (or uses a meta refresh)","Validate your redirects map before build: every parseable destination must match `/^https?:\\/\\//`"],"exampleFix":"// before — astro.config.mjs\nredirects: { '/contact': 'mailto:support@example.com' }\n\n// after\nredirects: { '/contact': 'https://example.com/contact' }\n// for mailto, link directly or serve a landing page instead of a redirect","handlingStrategy":"validation","validationCode":"// Validate the redirects map before build\nconst redirects = { '/contact': 'mailto:x@y.com', '/old': '/new' };\nfor (const [from, to] of Object.entries(redirects)) {\n  const dest = typeof to === 'string' ? to : to.destination;\n  if (!isSupportedRedirectDestination(dest)) {\n    throw new Error(`redirect ${from} → ${dest} must be http(s):// or an internal path`);\n  }\n}","typeGuard":"// External redirect destinations must be http(s); everything else is internal\nexport function isSupportedRedirectDestination(to: string): boolean {\n  return !URL.canParse(to) || /^https?:\\/\\//.test(to);\n}","tryCatchPattern":null,"preventionTips":["Keep config redirects to http(s) URLs or internal paths only","Handle mailto/app deep links with direct links or a landing page, never a redirect","Validate redirect maps imported from other platforms (nginx/Netlify) for scheme differences"],"tags":["redirects","config","routing","url-scheme"],"backgroundTag":"unsupported-redirect-scheme","analyzedSha":"e294953aa8aadd98d5be92e60a03037b05dbdfd4","analyzedAt":"2026-08-18T18:48:03.901Z","contentChangedAt":"2026-08-18T18:48:03.901Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}