Skip to content

Astro Middleware — Request Interception and Transformation

DodaTech Updated 2026-06-28 4 min read

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

  1. Forgetting to call next(): Middleware that doesn't call next() prevents the page from rendering. Always call it unless you're explicitly redirecting or returning an error response.
  2. Modifying response after returning it: Response modification must happen before returning. After return response, the response is immutable.
  3. Using middleware in static mode: Middleware only runs in server or hybrid mode. Static mode has no request-time processing.
  4. Not handling async errors: Middleware that throws crashes the server. Wrap logic in try/catch and return error responses.
  5. Putting too much logic in middleware: Middleware should be lightweight. Heavy computation in middleware slows every request.

Practice Questions

  1. Where do you define Astro middleware? Answer: In src/middleware.ts, exporting an onRequest function created with defineMiddleware().

  2. How do you redirect a user in middleware? Answer: Call context.redirect("/path", 302) instead of next(). This short-circuits the request.

  3. What is context.locals used for? Answer: Sharing data between middleware and page components. Set values in middleware, read them in pages.

  4. How do you chain multiple middleware functions? Answer: Use sequence() from astro/middleware to 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

Does middleware run for static pages?

: No. Middleware only runs for server-rendered or hybrid pages. Static HTML pages bypass middleware.

Can I use middleware with API endpoints?

: Yes. Middleware handles both page requests and API endpoints in SSR mode.

What happens if middleware throws an error?

: The request fails with a 500 error. Wrap middleware logic in try/catch and return appropriate error responses.

Can I access the database in middleware?

: Yes. Import database clients and query them. Keep operations fast to avoid slowing every request.

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