Middleware
Middleware is a function every request passes through before Astro renders the page. It is the place for work shared by all pages: identifying the visitor by a cookie, closing a section to guests, adding a header to the response.
A first middleware
Create the file src/middleware.ts; its name and location are fixed:
// src/middleware.ts
import { defineMiddleware } from 'astro:middleware';
export const onRequest = defineMiddleware(async (context, next) => {
console.log('Request:', context.url.pathname);
return next();
});Reload any page and the terminal running npm run dev shows a line like Request: /posts. The next() function passes the request on to the page and returns its response.
Data for pages
The context.locals object is available both in middleware and in pages as Astro.locals. Let us find the visitor's name from a cookie:
// src/middleware.ts
import { defineMiddleware } from 'astro:middleware';
export const onRequest = defineMiddleware(async (context, next) => {
context.locals.userName = context.cookies.get('user')?.value ?? null;
return next();
});For TypeScript to know about the new field, describe it in src/env.d.ts:
// src/env.d.ts
declare namespace App {
interface Locals {
userName: string | null;
}
}Now any page rendered on demand can write const { userName } = Astro.locals; and show a greeting such as "Hi, Anna!".
A protected section
Middleware can block access to a page and return its own response:
// src/middleware.ts
import { defineMiddleware } from 'astro:middleware';
export const onRequest = defineMiddleware(async (context, next) => {
if (context.url.pathname.startsWith('/admin') && !context.cookies.has('user')) {
return context.redirect('/login');
}
return next();
});A guest who opens /admin ends up at /login. next() is never called for them, and the page is not rendered.
Several functions
If there are several handlers, combine them with sequence() from the same module: export const onRequest = sequence(logger, auth);. They run in order.
When middleware runs
| Page | When middleware runs |
|---|---|
| Built ahead of time | Once, during the build |
| Rendered on demand | On every request |
Common mistake
People check a cookie in middleware for a static page and wonder why the protection does not work. For a prebuilt page the middleware ran during the build, when there was no visitor yet. Mark pages that depend on the visitor with export const prerender = false.