withastro/astro · error · AstroError

ForbiddenRewrite

ForbiddenRewrite

Error message

You tried to rewrite the on-demand route '${from}' with the static route '${to}', when using the 'server' output. 

The static route '${to}' is rendered by the component
'${component}', which is marked as prerendered. This is a forbidden operation because during the build, the component '${component}' is compiled to an
HTML file, which can't be retrieved at runtime by Astro.

What it means

ForbiddenRewrite: in 'server' output, Astro.rewrite() tried to route an on-demand (SSR) request to a prerendered route. Prerendered routes compile to static HTML at build time and are not present in the server manifest, so they cannot be served at runtime — the rewrite is forbidden (i18n fallback routes excepted).

Source

Thrown at packages/astro/src/core/rewrites/handler.ts:53

	state: FetchState,
	payload: RewritePayload,
	{ routeData, componentInstance, newUrl, pathname }: TryRewriteResult,
	{ mergeCookies = false }: { mergeCookies?: boolean } = {},
): void {
	const pipeline = state.pipeline;
	const oldPathname = state.pathname;

	// Disallow SSR→prerender rewrites: the prerendered route becomes a
	// static HTML file during build and isn't available in the server
	// manifest. Allow i18n fallback routes as an exception.
	const isI18nFallback = routeData.fallbackRoutes && routeData.fallbackRoutes.length > 0;
	if (
		pipeline.manifest.serverLike &&
		!state.routeData!.prerender &&
		routeData.prerender &&
		!isI18nFallback
	) {
		throw new AstroError({
			...ForbiddenRewrite,
			message: ForbiddenRewrite.message(state.pathname, pathname, routeData.component),
			hint: ForbiddenRewrite.hint(routeData.component),
		});
	}

	state.routeData = routeData;
	state.componentInstance = componentInstance;
	if (payload instanceof Request) {
		state.request = payload;
	} else {
		state.request = copyRequest(
			newUrl,
			state.request,
			routeData.prerender,
			pipeline.logger,
			state.routeData!.route,
		);

View on GitHub (pinned to d081033d5f)

Solutions

  1. Add `export const prerender = false` to the destination component so it renders on demand.
  2. Use Astro.redirect() instead of Astro.rewrite() when the target must stay static (a real navigation, not an internal rewrite).
  3. Re-check whether the destination route is in the SSR server manifest.

Example fix

// before — destination pages/about.astro is prerendered
Astro.rewrite('/about');

// after — make the destination on-demand
// pages/about.astro
export const prerender = false;
Defensive patterns

Strategy: validation

Validate before calling

function canRewriteTo(fromPrerender: boolean, toPrerender: boolean, serverLike: boolean, isI18nFallback: boolean): boolean {
  if (serverLike && !fromPrerender && toPrerender && !isI18nFallback) return false;
  return true;
}
// before Astro.rewrite, ensure the destination is on-demand

Try / catch

try {
  Astro.rewrite(target);
} catch (e) {
  if (e instanceof AstroError && e.code === 'ForbiddenRewrite') {
    return Astro.redirect(target); // fall back to a real navigation
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling Astro.rewrite('/static-page') from an SSR route where /static-page is prerendered; a prerendered destination route while output is 'server'; middleware rewriting on-demand requests to prerendered pages.

Common situations: Migrating pages between prerendered and on-demand; using Astro.rewrite to route to a page intended to be static; forgetting that output:'server' still prerenders pages without `export const prerender = false` being absent (i.e. the target defaulted to prerender).

Understand the failure class

Related errors


AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12). Data as JSON: /api/errors/a5621a3b97dd6fcc. Report an issue: GitHub.