{"record":{"id":"a5621a3b97dd6fcc","repo":"withastro/astro","slug":"forbiddenrewrite-a5621a","errorCode":"ForbiddenRewrite","errorMessage":"You tried to rewrite the on-demand route '${from}' with the static route '${to}', when using the 'server' output. \n\nThe static route '${to}' is rendered by the component\n'${component}', which is marked as prerendered. This is a forbidden operation because during the build, the component '${component}' is compiled to an\nHTML file, which can't be retrieved at runtime by Astro.","messagePattern":"You tried to rewrite the on-demand route '(.+?)' with the static route '(.+?)', when using the 'server' output\\. \n\nThe static route '(.+?)' is rendered by the component\n'(.+?)', which is marked as prerendered\\. This is a forbidden operation because during the build, the component '(.+?)' is compiled to an\nHTML file, which can't be retrieved at runtime by Astro\\.","errorType":"exception","errorClass":"AstroError","httpStatus":null,"severity":"error","filePath":"packages/astro/src/core/rewrites/handler.ts","lineNumber":53,"sourceCode":"export function applyRewriteToState(\n\tstate: FetchState,\n\tpayload: RewritePayload,\n\t{ routeData, componentInstance, newUrl, pathname }: TryRewriteResult,\n\t{ mergeCookies = false }: { mergeCookies?: boolean } = {},\n): void {\n\tconst oldPathname = state.pathname;\n\n\t// Disallow SSR→prerender rewrites: the prerendered route becomes a\n\t// static HTML file during build and isn't available in the server\n\t// manifest. Allow i18n fallback routes as an exception.\n\tconst isI18nFallback = routeData.fallbackRoutes && routeData.fallbackRoutes.length > 0;\n\tif (\n\t\tstate.manifest.serverLike &&\n\t\t!state.routeData!.prerender &&\n\t\trouteData.prerender &&\n\t\t!isI18nFallback\n\t) {\n\t\tthrow new AstroError({\n\t\t\t...ForbiddenRewrite,\n\t\t\tmessage: ForbiddenRewrite.message(state.pathname, pathname, routeData.component),\n\t\t\thint: ForbiddenRewrite.hint(routeData.component),\n\t\t});\n\t}\n\n\tstate.routeData = routeData;\n\tstate.componentInstance = componentInstance;\n\tif (payload instanceof Request) {\n\t\tstate.request = payload;\n\t} else {\n\t\tstate.request = copyRequest(\n\t\t\tnewUrl,\n\t\t\tstate.request,\n\t\t\trouteData.prerender,\n\t\t\tstate.logger,\n\t\t\tstate.routeData!.route,\n\t\t);","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/withastro/astro/blob/e294953aa8aadd98d5be92e60a03037b05dbdfd4/packages/astro/src/core/rewrites/handler.ts#L35-L71","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before — src/middleware.ts (output: 'server', /login is prerendered)\nexport const onRequest = (context, next) => {\n  if (context.url.pathname.startsWith('/admin')) return rewrite('/login');\n  return next();\n};\n\n// after\nimport { redirect } from 'astro:middleware';\nexport const onRequest = (context, next) => {\n  if (context.url.pathname.startsWith('/admin')) return redirect('/login');\n  return next();\n};\n// or remove `export const prerender = true` from src/pages/login.astro","handlingStrategy":"fallback","validationCode":"// Keep an explicit list of prerendered paths and guard rewrites against it\nconst PRERENDERED = new Set(['/about', '/pricing']); // keep in sync with prerender flags\nexport const onRequest = async (context, next) => {\n  if (needsRewrite(context) && !PRERENDERED.has(target)) {\n    return rewrite(target);\n  }\n  return next();\n};","typeGuard":null,"tryCatchPattern":"// src/middleware.ts — degrade rewrites-to-static into redirects\nexport const onRequest = async (context, next) => {\n  try {\n    return await next();\n  } catch (err) {\n    if (err instanceof Error && /forbidden operation/i.test(err.message)) {\n      return context.redirect(context.url.pathname); // or a safe on-demand route\n    }\n    throw err;\n  }\n};","preventionTips":["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"],"tags":["rewrites","middleware","prerender","ssr","hybrid-output"],"backgroundTag":"forbidden-rewrite-to-static-route","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"}