{"record":{"id":"45a03e27a9ee5589","repo":"withastro/astro","slug":"redirectwithnolocation","errorCode":"RedirectWithNoLocation","errorMessage":"A redirect must be given a location with the `Location` header.","messagePattern":"A redirect must be given a location with the `Location` header\\.","errorType":"exception","errorClass":"AstroError","httpStatus":null,"severity":"error","filePath":"packages/astro/src/core/redirects/validate.ts","lineNumber":7,"sourceCode":"import { AstroError, AstroErrorData } from '../errors/index.js';\n\nexport function getRedirectLocationOrThrow(headers: Headers): string {\n\tlet location = headers.get('location');\n\n\tif (!location) {\n\t\tthrow new AstroError({\n\t\t\t...AstroErrorData.RedirectWithNoLocation,\n\t\t});\n\t}\n\n\treturn location;\n}\n","sourceCodeStart":1,"sourceCodeEnd":14,"githubUrl":"https://github.com/withastro/astro/blob/52e6c34790cc8ac4e69e6135ace06049867e5c4a/packages/astro/src/core/redirects/validate.ts#L1-L14","documentation":"During a static build, when a prerendered route returns a 3xx response Astro writes an HTML redirect stub and must know where to point it, so `getRedirectLocationOrThrow` reads the `Location` header and throws `RedirectWithNoLocation` (an AstroError) when it is null. The helper is invoked from the build's response handling (`core/build/generate.ts`), so this surfaces at build time for prerendered pages/endpoints that return a redirect status without a destination.","triggerScenarios":"A prerendered page or endpoint returning `new Response(body, { status: 302 })` with no `Location` header; middleware (running during prerender) returning a bare 3xx status without location; code that copies a Response's status but drops its headers before returning it.","commonSituations":"Hand-built redirect Responses instead of `Response.redirect()`; an upstream fetch returning 3xx and the handler forwarding only `response.status`; middleware logic branches that set status but forget the header for some routes.","solutions":["Use `Response.redirect(new URL('/target', context.url), 302)` which always sets `Location`","Or add the header explicitly: `new Response(body, { status: 302, headers: { Location: '/target' } })`","If forwarding an upstream redirect, propagate its `Location` header (or just return the upstream Response)","Cover every 3xx branch in middleware with a header — including fallback/else paths"],"exampleFix":"// before\nreturn new Response('Redirecting…', { status: 302 });\n\n// after\nreturn Response.redirect(new URL('/new-home', context.url), 302);\n// or: new Response('Redirecting…', { status: 302, headers: { Location: '/new-home' } })","handlingStrategy":"type-guard","validationCode":"// before returning a 3xx from a prerendered page/endpoint/middleware\nif (response.status >= 300 && response.status < 400) {\n  if (!isRedirectWithLocation(response)) {\n    response = Response.redirect(new URL('/fallback', context.url), response.status);\n  }\n}","typeGuard":"// A 3xx is only a usable redirect if it carries a Location header\nexport function isRedirectWithLocation(res: Response): boolean {\n  return res.status >= 300 && res.status < 400 && res.headers.get('location') !== null;\n}","tryCatchPattern":"// Middleware safety net during prerender: never let a bare 3xx reach the build\nexport const onRequest = async (context, next) => {\n  const res = await next();\n  if (res?.status >= 300 && res?.status < 400 && !res.headers.get('location')) {\n    return Response.redirect(new URL('/', context.url), res.status);\n  }\n  return res;\n};","preventionTips":["Prefer `Response.redirect(url, status)` — it cannot produce a location-less redirect","When forwarding upstream responses, forward headers too (or return the upstream Response whole)","Unit-test middleware branches that return 3xx statuses, asserting `Location` presence"],"tags":["redirect","http-headers","prerender","build"],"backgroundTag":"missing-redirect-location-header","analyzedSha":"52e6c34790cc8ac4e69e6135ace06049867e5c4a","analyzedAt":"2026-08-18T18:48:03.901Z","contentChangedAt":"2026-08-18T18:48:03.901Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}