withastro/astro · error · AstroError
UnsupportedExternalRedirect
UnsupportedExternalRedirect
Error message
The destination URL in the external redirect from "${from}" to "${to}" is unsupported. What it means
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.
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?:\/\//`
Example fix
// before — astro.config.mjs
redirects: { '/contact': 'mailto:support@example.com' }
// after
redirects: { '/contact': 'https://example.com/contact' }
// for mailto, link directly or serve a landing page instead of a redirect Defensive patterns
Strategy: validation
Validate before calling
// Validate the redirects map before build
const redirects = { '/contact': 'mailto:x@y.com', '/old': '/new' };
for (const [from, to] of Object.entries(redirects)) {
const dest = typeof to === 'string' ? to : to.destination;
if (!isSupportedRedirectDestination(dest)) {
throw new Error(`redirect ${from} → ${dest} must be http(s):// or an internal path`);
}
} Type guard
// External redirect destinations must be http(s); everything else is internal
export function isSupportedRedirectDestination(to: string): boolean {
return !URL.canParse(to) || /^https?:\/\//.test(to);
} Prevention
- 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
When it happens
Trigger: `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.
Common situations: 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.
Related errors
- Error generating redirects
- IncorrectStrategyForI18n
- InvalidI18nMiddlewareConfiguration
- InvalidRedirectDestination
- MissingIndexForInternationalizationError
AI-assisted analysis of withastro/astro@e294953aa8 (2026-08-18).
Data as JSON: /api/errors/82c233b5d40e0849.
Report an issue: GitHub.
Appendix: source
Thrown at packages/astro/src/core/routing/create-manifest.ts:542
const pathname = segments.every((segment) => segment.length === 1 && !segment[0].dynamic)
? `/${segments.map((segment) => segment[0].content).join('/')}`
: null;
const params = segments
.flat()
.filter((p) => p.dynamic)
.map((p) => p.content);
const route = joinSegments(segments);
let destination: string;
if (typeof to === 'string') {
destination = to;
} else {
destination = to.destination;
}
// check if the link starts with http or https; if not, throw an error
if (URL.canParse(destination) && !/^https?:\/\//.test(destination)) {
throw new AstroError({
...UnsupportedExternalRedirect,
message: UnsupportedExternalRedirect.message(from, destination),
});
}
const redirectRoute = routeMap.get(destination);
// If the source has dynamic params and the redirect will be prerendered,
// we need a valid redirectRoute to map them. Without it, the build will fail
// later with a misleading error. Catch this early and provide a clear error message.
if (
params.length > 0 &&
!redirectRoute &&
!URL.canParse(destination) &&
getPrerenderDefault(config)
) {
throw new AstroError({
...InvalidRedirectDestination,View on GitHub (pinned to e294953aa8)