Middleware
Intercept every request and response — add auth checks, inject locals, rewrite URLs, and set headers globally.
What is Astro Middleware?
Middleware is a function that runs on every request, before any page or API route
is rendered. It receives the full RequestContext and a next() callback.
You can:
- Inject data into
Astro.locals(e.g. current user) - Redirect unauthenticated users
- Add/modify response headers
- Rewrite request URLs
- Log requests for analytics
Astro middleware runs on every request before any page
renders. It can inject data into Astro.locals,
redirect users, or add response headers.
See middleware/index.ts skeleton
export const onRequest = defineMiddleware(async (context, next) => {
context.locals.requestId = Math.random().toString(36)
context.locals.authenticated = await checkAuth(context)
return next() // continues to the page
})
Creating middleware
Create src/middleware.ts
Astro auto-discovers src/middleware.ts (or .js). No configuration needed.
// src/middleware.ts
import { defineMiddleware } from 'astro:middleware'
export const onRequest = defineMiddleware(async (context, next) => {
// Runs before EVERY page and API route
// 1. Inject a request ID into locals
context.locals.requestId = crypto.randomUUID()
// 2. Check auth (example)
const session = context.cookies.get('session')
context.locals.user = session ? await getUserFromSession(session.value) : null
// 3. Guard a route
if (context.url.pathname.startsWith('/admin') && !context.locals.user) {
return context.redirect('/login')
}
// 4. Continue to the page
const response = await next()
// 5. Add a custom header to every response
response.headers.set('X-Request-Id', context.locals.requestId)
return response
}) Use locals in your page
Anything you set on context.locals is available as Astro.locals in every
.astro file and API route.
---
// Any page or layout
const { user, requestId } = Astro.locals
---
{user ? <p>Hello, {user.name}!</p> : <p>Guest</p>} Type your locals (optional but recommended)
Extend the App.Locals interface in src/env.d.ts for full TypeScript support.
// src/env.d.ts
declare namespace App {
interface Locals {
requestId: string
user: { id: number; name: string; email: string } | null
}
} Middleware sequence
Multiple middleware functions can be chained using sequence():
import { defineMiddleware, sequence } from 'astro:middleware'
const logging = defineMiddleware(async (ctx, next) => {
console.log(`→ ${ctx.request.method} ${ctx.url.pathname}`)
return next()
})
const auth = defineMiddleware(async (ctx, next) => {
// ...auth logic
return next()
})
export const onRequest = sequence(logging, auth)
Middleware runs on every request, including API routes. Keep it fast — avoid blocking I/O unless necessary.
Middleware only works with output: 'server' or output: 'hybrid'.
Static pages are pre-built and never pass through middleware at request time.