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
With `output: 'server'`, an on-demand (non-prerendered) route may not `rewrite()` to a prerendered route: during build the target component is compiled to a static HTML file and is absent from the runtime server manifest, so nothing can render it on demand. The rewrite handler therefore throws `ForbiddenRewrite` before swapping state, with an explicit exception for i18n fallback routes (`fallbackRoutes` non-empty).
Solutions
- Make the rewrite target on-demand: remove `export const prerender = true` from that page (it must be renderable at runtime)
- Use `redirect()` instead of `rewrite()` — a redirect lets the client fetch the static HTML normally
- Move the shared content into a component or API both routes consume, instead of rewriting between output modes
Example fix
// before — src/middleware.ts (output: 'server', /login is prerendered)
export const onRequest = (context, next) => {
if (context.url.pathname.startsWith('/admin')) return rewrite('/login');
return next();
};
// after
import { redirect } from 'astro:middleware';
export const onRequest = (context, next) => {
if (context.url.pathname.startsWith('/admin')) return redirect('/login');
return next();
};
// or remove `export const prerender = true` from src/pages/login.astro Defensive patterns
Strategy: fallback
Validate before calling
// Keep an explicit list of prerendered paths and guard rewrites against it
const PRERENDERED = new Set(['/about', '/pricing']); // keep in sync with prerender flags
export const onRequest = async (context, next) => {
if (needsRewrite(context) && !PRERENDERED.has(target)) {
return rewrite(target);
}
return next();
}; Try / catch
// src/middleware.ts — degrade rewrites-to-static into redirects
export const onRequest = async (context, next) => {
try {
return await next();
} catch (err) {
if (err instanceof Error && /forbidden operation/i.test(err.message)) {
return context.redirect(context.url.pathname); // or a safe on-demand route
}
throw err;
}
}; Prevention
- Audit middleware rewrites after flipping any route's `prerender` flag
- Prefer `redirect()` when the target might be static — redirects work across output modes
- Centralize prerender decisions (one file exporting which routes are static) and review it in code review
When it happens
Trigger: Middleware on an SSR route returning `rewrite('/about')` where `src/pages/about.astro` has `export const prerender = true`; an on-demand page rewriting to any prerendered page under server output; hybrid apps where marketing pages are static but a global middleware rewrites into them.
Common situations: Adding auth middleware that rewrites to a prerendered `/login`; incrementally adopting islands/hybrid prerender while keeping old rewrites; migrating from static to server output without auditing rewrite targets.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- ForbiddenRewrite
- A collision will result in a hard error in following…
- ActionCalledFromServerError
- ActionCalledFromServerError
- AdapterSupportOutputMismatch
AI-assisted analysis of withastro/astro@e294953aa8 (2026-08-18).
Data as JSON: /api/errors/a5621a3b97dd6fcc.
Report an issue: GitHub.
Appendix: source
Thrown at packages/astro/src/core/rewrites/handler.ts:53
export function applyRewriteToState(
state: FetchState,
payload: RewritePayload,
{ routeData, componentInstance, newUrl, pathname }: TryRewriteResult,
{ mergeCookies = false }: { mergeCookies?: boolean } = {},
): void {
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 (
state.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,
state.logger,
state.routeData!.route,
);View on GitHub (pinned to e294953aa8)