Skip to content
Now accepting new projects — limited slots available. Get started →
Architecture · Updated Jul 31, 2026

What is Middleware?

Middleware is a software layer that intercepts and processes requests between a client and an application's core logic.

What is Middleware?

Middleware is code that intercepts requests before they hit your route handlers. It's been around since the 1960s in message-oriented systems, but in web dev it means functions that run in sequence during the request lifecycle. In Next.js (stable since v13), middleware runs at the edge before your page or API route resolves—perfect for auth checks, redirects, A/B tests, geo-routing. Cold starts under a millisecond. In Express, middleware is the framework—every app.use() registers one. Common pattern: verify a JWT in middleware so your handlers never see unauthenticated requests.

How it works

Middleware functions get a request, optionally modify it, then either pass control to the next function or return a response. Execution model varies by framework.

Express.js pattern:

// Express middleware — runs on every request
app.use((req, res, next) => {
  const token = req.headers.authorization?.split(' ')[1];
  if (!token) return res.status(401).json({ error: 'No token' });
  try {
    req.user = jwt.verify(token, process.env.JWT_SECRET);
    next(); // pass to next middleware or route handler
  } catch {
    res.status(403).json({ error: 'Invalid token' });
  }
});

Next.js pattern (middleware.ts at project root):

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  const country = request.geo?.country || 'US';
  if (country === 'DE') {
    return NextResponse.redirect(new URL('/de', request.url));
  }
  return NextResponse.next();
}

export const config = {
  matcher: ['/((?!_next|favicon.ico).*)'],
};

Next.js middleware runs in the Edge Runtime—a V8 isolate with no Node.js APIs like fs or net. Keeps execution fast (typically under 5ms at Vercel's edge) but limits what you can do. You can't query a traditional database directly. You'd use an edge-compatible client like Neon's serverless driver or Upstash Redis.

The mental model: middleware is a pipeline. Each function continues the chain or short-circuits it. Order matters. Run auth middleware before rate-limiting if you want to rate-limit per user.

When to use it

Middleware shines when you need logic across many routes without copy-pasting into every handler.

Use middleware when:

  • Authentication/authorization checks need to happen before any route logic
  • You need request-level redirects (locale, A/B tests, feature flags)
  • Adding headers like CSP, CORS, or cache-control across all responses
  • Logging and request tracing (attach a request ID early in the pipeline)
  • Rate limiting at the edge before hitting your origin server

Skip middleware when:

  • Logic is specific to a single route—just put it in the handler
  • You need heavy computation or long-running processes (middleware should be fast)
  • You're tempted to put business logic there—middleware is infrastructure, not domain logic
  • You need full Node.js APIs in Next.js (Edge Runtime restrictions will bite you)

We've shipped middleware on 50+ projects. Rule of thumb: if you're copy-pasting the same 5 lines into multiple route handlers, it belongs in middleware.

Middleware vs alternatives

Approach Runs where Scope Typical latency Best for
Next.js Middleware Edge (V8 isolate) All matched routes <5ms Redirects, auth gates, geo-routing
API Route handler Node.js / Edge Single endpoint 10-100ms Business logic, DB queries
Express middleware Node.js server Configurable 1-50ms Full-stack Node apps
Reverse proxy (nginx/Cloudflare) Infrastructure layer All traffic <1ms Rate limiting, WAF, SSL termination
Higher-order components (React) Client-side Per component N/A (client) UI-level auth guards

Next.js middleware replaced the custom server setups that were common pre-v12. Astro added middleware in 2.6 (2023) with a similar onRequest hook model.

Real-world example

On a SaaS we shipped in early 2026, we used Next.js 15 middleware to handle multi-tenant routing. The app served 12 white-label clients from a single deployment. Middleware read the Host header, looked up the tenant config from Upstash Redis (edge-compatible, ~2ms round trip), and rewrote the request to the correct tenant-specific layout.

Without middleware, we'd have needed either 12 separate deployments or a clunky client-side tenant resolution that would've added 200-400ms to every page load. The middleware approach kept TTFB under 120ms globally across Vercel's edge network. Total middleware execution time averaged 4ms per request based on our observability data from Axiom.

Frequently asked questions about Middleware

Is middleware the same as an API route?
No. Middleware intercepts requests before they reach an API route or page handler. It's designed for cross-cutting concerns like auth, redirects, and header manipulation. An API route is an endpoint that handles a specific request and returns a response — it's where your business logic lives. Think of middleware as the bouncer at the door and the API route as the person you're actually there to meet. In Next.js, middleware runs in the Edge Runtime while API routes can run in either Edge or Node.js runtimes, giving API routes access to a broader set of APIs.
When did middleware become standard in Next.js?
Next.js introduced middleware as a beta feature in version 12.0 (October 2021). It became stable in v12.2 (June 2022) with significant API changes — notably moving from the `_middleware.ts` file convention inside page directories to a single `middleware.ts` file at the project root. This was a breaking change that tripped up a lot of teams. By Next.js 13 and the App Router (2023), middleware was a settled pattern. As of Next.js 15 (late 2025), the API hasn't changed much, which is a good sign of maturity.
What's the alternative to middleware for authentication?
The main alternatives are: (1) checking auth inside each API route or server component directly, (2) using a reverse proxy like Cloudflare Access or AWS ALB rules to gate access at the infrastructure level, or (3) handling it client-side with route guards in React. Our preferred approach is a layered one — middleware for the fast rejection of obviously unauthenticated requests (no token, expired token), and then a second check in the server component or API route for fine-grained authorization (does this user have access to this resource?). Relying solely on client-side auth is a security risk since it's trivially bypassed.
Can Next.js middleware access a database?
Not with traditional database drivers. Next.js middleware runs in the Edge Runtime, which is a stripped-down V8 environment without Node.js APIs like `net` or `fs` that most database clients depend on. However, you can use edge-compatible clients: Neon's serverless Postgres driver (over HTTP/WebSocket), PlanetScale's serverless driver, Upstash Redis, or Turso's libSQL HTTP client. These all communicate over HTTP or WebSocket, which the Edge Runtime supports. Keep queries minimal though — middleware should stay fast. If you need a complex database query, let the request pass through to a Node.js-based API route instead.
Get in touch

Let's build
something together.

Whether it's a migration, a new build, or an SEO challenge — the Social Animal team would love to hear from you.

Get in touch →