Astro Middleware — Request Interception and Transformation
In this tutorial, you will learn about Astro Middleware. We cover key concepts, practical examples, and best practices to help you master this topic.
Learn Astro middleware: intercept requests, add headers, authenticate users, redirect, and modify responses for server-rendered pages in SSR mode.
In this lesson, you'll create middleware functions in src/middleware.ts to handle authentication, logging, header injection, and request validation before pages render.
What You'll Learn
How to define middleware, chain multiple middleware functions, access request and context, redirect unauthenticated users, and modify response headers.
Why It Matters
Middleware centralizes cross-cutting concerns like authentication, logging, and security headers. Instead of duplicating code in every page, apply it once in middleware.
Real-World Use
DodaTech uses Astro middleware to verify authentication tokens, set security headers, and log request metrics across all server-rendered pages.
flowchart LR
A[Incoming Request] --> B[Middlware 1: Auth]
B --> C[Middlware 2: Logging]
C --> D[Middlware 3: Headers]
D --> E[Page Render]
style B fill:#ff5a03,color:#fff
Basic Middleware
Create src/middleware.ts:
import { defineMiddleware } from "astro/middleware";
export const onRequest = defineMiddleware(async (context, next) => {
const start = Date.now();
const response = await next();
const duration = Date.now() - start;
console.log(`${context.request.method} ${context.url.pathname} - ${duration}ms`);
return response;
});
Every request to a server-rendered page passes through this middleware. It logs the method, path, and response time.
Authentication Middleware
Redirect unauthenticated users:
import { defineMiddleware } from "astro/middleware";
export const onRequest = defineMiddleware(async (context, next) => {
const authToken = context.cookies.get("session");
if (context.url.pathname.startsWith("/dashboard")) {
if (!authToken) {
return context.redirect("/login", 302);
}
// Validate token...
}
return next();
});
Users without a session cookie who try to access /dashboard/* are redirected to /login.
Adding Security Headers
Modify response headers:
import { defineMiddleware } from "astro/middleware";
export const onRequest = defineMiddleware(async (context, next) => {
const response = await next();
response.headers.set("X-Content-Type-Options", "nosniff");
response.headers.set("X-Frame-Options", "DENY");
response.headers.set("X-XSS-Protection", "1; mode=block");
response.headers.set("Referrer-Policy", "strict-origin-when-cross-origin");
return response;
});
These security headers protect against common web vulnerabilities without any per-page configuration.
Middleware with Multiple Functions
Chain middleware using an array:
import { defineMiddleware, sequence } from "astro/middleware";
const authMiddleware = defineMiddleware(async (context, next) => {
if (context.url.pathname.startsWith("/admin")) {
const user = await authenticate(context);
if (!user) return context.redirect("/login");
context.locals.user = user;
}
return next();
});
const loggingMiddleware = defineMiddleware(async (context, next) => {
console.log(`${context.request.method} ${context.url.pathname}`);
return next();
});
const headersMiddleware = defineMiddleware(async (context, next) => {
const response = await next();
response.headers.set("X-Powered-By", "Astro");
return response;
});
export const onRequest = sequence(
authMiddleware,
loggingMiddleware,
headersMiddleware
);
Middleware runs in the order specified in sequence(). Each function can decide whether to continue the chain or short-circuit.
Context Locals
Share data between middleware and pages:
// middleware.ts
export const onRequest = defineMiddleware(async (context, next) => {
context.locals.user = await getUser(context.cookies.get("token"));
return next();
});
// In a page
---
const user = Astro.locals.user;
---
<h1>Welcome, {user?.name ?? "Guest"}</h1>
The locals object survives the request lifecycle and is available in all pages and components.
Common Mistakes
- Forgetting to call
next(): Middleware that doesn't callnext()prevents the page from rendering. Always call it unless you're explicitly redirecting or returning an error response. - Modifying response after returning it: Response modification must happen before returning. After
return response, the response is immutable. - Using middleware in static mode: Middleware only runs in server or hybrid mode. Static mode has no request-time processing.
- Not handling async errors: Middleware that throws crashes the server. Wrap logic in try/catch and return error responses.
- Putting too much logic in middleware: Middleware should be lightweight. Heavy computation in middleware slows every request.
Practice Questions
Where do you define Astro middleware? Answer: In
src/middleware.ts, exporting anonRequestfunction created withdefineMiddleware().How do you redirect a user in middleware? Answer: Call
context.redirect("/path", 302)instead ofnext(). This short-circuits the request.What is
context.localsused for? Answer: Sharing data between middleware and page components. Set values in middleware, read them in pages.How do you chain multiple middleware functions? Answer: Use
sequence()fromastro/middlewareto compose multiple middleware functions in order.
Challenge
Create a middleware chain that: logs every request, sets security headers, checks authentication for /admin/* routes, and injects the current user into context.locals.
Mini Project
Build an admin panel with authentication middleware: protect all /admin routes with a cookie-based auth check, add request logging, and include security headers on every response.
FAQ
What's Next
Learn about Astro DB for adding database storage to your Astro applications.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro