Astro Middleware vs. Adapter Static Asset Serving: Auth Bypass Risk in Hybrid Rendering
0 reputation · 27 Jul 2022, 19:32 UTC
0 reputation · 27 Jul 2022, 19:32 UTC
Astro's hybrid rendering mode (output: 'hybrid') splits routes into prerendered static pages and server-rendered endpoints. Middleware is intended to provide cross-cutting concerns like authentication, but the framework delegates static asset serving to the deployment adapter (Node, Vercel, Netlify, Cloudflare). This creates an integration boundary where middleware execution guarantees diverge from developer expectations.
Prerendered pages generate HTML at build time; middleware never executes for them at runtime. Static files in public/ are served directly by the adapter, bypassing Astro middleware entirely. The astro:config:setup API cannot distinguish static from dynamic routes at build time, so middleware authors cannot automatically skip auth checks for prerendered pages without maintaining manual route lists. Adapter behavior varies: the Node adapter runs middleware for all requests, while edge adapters may skip middleware for static assets.
public/ assets and prerendered routes?protected route convention?33275 reputation · 28 Jul 2022, 03:38 UTC
No deployment adapter guarantees middleware execution for every single request path. By design, Astro separates the application runtime from the static asset server. Assets located in the public/ directory are served directly by the adapter (or the underlying CDN/web server), bypassing the Astro middleware pipeline entirely. Similarly, in output: 'hybrid' mode, pages marked with export const prerender = true are generated as static HTML files at build time; these are served as static assets and do not trigger server-side middleware during runtime requests.
The divergence in behavior stems from the architectural goal of maximizing performance. Serving a static file from a CDN or a filesystem is significantly faster than booting a server-side function to execute middleware. Consequently, the "Integration Boundary" is a hard line: if the adapter identifies a request as a static file (whether from public/ or a prerendered route), it fulfills the request before the Astro runtime is ever invoked.
Because there is no declarative configuration to "opt-in" static assets to middleware, developers must handle exclusion logic within the middleware function itself. To avoid hardcoding exhaustive lists, use pattern matching against the request.url:
export const onRequest = defineMiddleware(({ request }, next) => {
const url = new URL(request.url);
// Exclude common static asset extensions or the /favicon.ico path
if (url.pathname.includes('.') || url.pathname.startsWith('/_astro')) {
return next();
}
// Perform auth checks for remaining routes
return handleAuth(request, next);
});
Since Astro lacks a stable protected route convention, library authors should document security requirements using a "Shared Responsibility Model":
public/ directory or prerendered pages.public/ while unauthenticated.useEffect or onMount checks) for prerendered pages that require a session, while emphasizing that this is for UX, not hard security.To confirm if your current adapter is bypassing middleware for specific routes, add a temporary logging statement to your middleware and test the following:
public/ (e.g., /robots.txt).prerender = true.If the log does not appear for the first two, your adapter is serving them statically.
Missing Diagnostic: To provide a more specific adapter recommendation, please specify if you are using a Node-based server or an Edge-based provider (Vercel, Netlify, Cloudflare).
Use comments to ask for clarification. Post a solution as an answer.
33,275 reputation · 28 Jul 2022, 06:58 UTC
In Astro v2.x the Node adapter runs the middleware pipeline for every request, including files from public/ and prerendered pages. Edge adapters (Vercel, Netlify, Cloudflare) will short‑circuit the middleware when the request is detected as a static asset unless you explicitly route the asset through the Node runtime or add a rewrite rule.
Place assets you want to bypass in a dedicated folder such as public/_static and add a minimal adapter rule. For Vercel:
// vercel.json
{
"rewrites": [
{ "source": "/_static/:path*", "destination": "/index.html" }
]
}
This keeps the asset out of the middleware chain while still benefiting from CDN caching.
Library authors should state the adapter limitation in the README, provide the example rewrite, and recommend a runtime guard such as if (!request.nextUrl.pathname.startsWith('/_static')) before performing auth.