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

Thrown during a rewrite inside sequenced middleware when an on-demand (SSR) route tries to rewrite to a prerendered (SSG) route while output is 'server'. Prerendered routes are emitted as static HTML files at build time and are not in the server manifest, so the runtime cannot render them on demand. The check requires serverLike === true, the source isPrerendered === false, and the target routeData.prerender === true.

Source

Thrown at packages/astro/src/core/middleware/sequence.ts:66

								handleContext.request.clone(),
							);
						}
						const oldPathname = handleContext.url.pathname;
						const pipeline: Pipeline = Reflect.get(handleContext, pipelineSymbol);
						const { routeData, pathname } = await pipeline.tryRewrite(
							payload,
							handleContext.request,
						);

						// This is a case where the user tries to rewrite from a SSR route to a prerendered route (SSG).
						// This case isn't valid because when building for SSR, the prerendered route disappears from the server output because it becomes an HTML file,
						// so Astro can't retrieve it from the emitted manifest.
						if (
							pipeline.manifest.serverLike === true &&
							handleContext.isPrerendered === false &&
							routeData.prerender === true
						) {
							throw new AstroError({
								...ForbiddenRewrite,
								message: ForbiddenRewrite.message(
									handleContext.url.pathname,
									pathname,
									routeData.component,
								),
								hint: ForbiddenRewrite.hint(routeData.component),
							});
						}

						carriedPayload = payload;
						handleContext.request = newRequest;
						handleContext.url = new URL(newRequest.url);
						handleContext.params = getParams(routeData, pathname);
						handleContext.routePattern = routeData.route;
						setOriginPathname(
							handleContext.request,
							oldPathname,

View on GitHub (pinned to d081033d5f)

Solutions

  1. Make the target route server-rendered (remove `export const prerender = true`) so it exists in the server manifest.
  2. Use a redirect (HTTP) to the static HTML file instead of an in-rewrite, if the static route must stay prerendered.
  3. Reorganize routes so SSR-to-prerendered rewrites are unnecessary.

Example fix

// before - target page has prerender = true, source is SSR
return context.next('/static-page'); // ForbiddenRewrite

// after - remove prerender from target, or use a redirect
return context.redirect('/static-page');
Defensive patterns

Strategy: validation

Validate before calling

// Verify target route is server-rendered before rewriting.
const pipeline = Reflect.get(context, pipelineSymbol);
const { routeData } = await pipeline.tryRewrite(target, context.request);
if (pipeline.manifest.serverLike && routeData.prerender) {
  // use redirect instead of rewrite
  return context.redirect(target);
}
return context.next(target);

Type guard

function isRewritableTarget(routeData: { prerender?: boolean }, serverLike: boolean): boolean {
  return !(serverLike && routeData.prerender === true);
}

Try / catch

try {
  return await context.next('/some-route');
} catch (e) {
  if (e instanceof Error && /forbidden operation/i.test(e.message)) {
    return context.redirect('/some-route'); // static HTML exists
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling next(rewritePayload) (or returning a rewrite) from an SSR page/middleware to a route that has `export const prerender = true`, in a project with output 'server'. The ForbiddenRewrite error names the from-URL, the to-URL, and the target component.

Common situations: Mixing prerendered and on-demand routes and using rewrites to navigate between them; marking a route prerender=true but linking to it dynamically; refactoring routing without considering prerender status.

Understand the failure class

Related errors


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