Hirefullstack – Software Engineering & IT-Beratung aus Berlin
← Back to overview
Advanced 10 min read

Middleware

Code running before the page – and therefore it has to be fast

In one sentence

A middleware.ts runs before every matching request. Good for redirects and rough access checks – not for database queries.

MiddlewareCode running before every matching request – for redirects, languages or access control.It runs in a restricted environment: no database access, no Node modules. sits in front of your pages. It may redirect, rewrite or set headers – before it is even decided which page gets rendered.

middleware.ts
import { NextResponse, type NextRequest } from "next/server";

export function middleware(request: NextRequest) {
  const signedIn = request.cookies.has("session");

  if (!signedIn) {
    const target = new URL("/sign-in", request.url);
    target.searchParams.set("next", request.nextUrl.pathname);
    return NextResponse.redirect(target);
  }

  return NextResponse.next();
}

// Only for these addresses – important for speed
export const config = {
  matcher: ["/account/:path*", "/admin/:path*"],
};
The file lives in the project root, not in app/.
Careful The matcher is not optional

Without it the middleware runs on every request – including images, fonts and scripts. That costs time on every single call. Always narrow it to the addresses you actually mean.

What it can and cannot do

Reach for it when …
  • +

    Redirecting: not signed in → to the sign-in page

  • +

    Deriving language or region from the request

  • +

    A/B tests: set a cookie and rewrite internally

  • +

    Setting headers, a content security policy for instance

Skip it when …
  • Database queries – the environment simply cannot

  • Full permission checks; it is too coarse for that

  • Heavy computation – everything here delays every request

Pitfall Middleware is not access control

It checks whether a session cookie exists at all – not whether it is valid and what the person may do. The real check belongs in the page, the server action or the route handler. Middleware is the doorman, not the lock.

The restricted environment

limits.txt
Available:      fetch, Web Crypto, URL, cookies, headers
Not available:  Node modules (fs, path), database drivers,
                long-running work

Reason: middleware runs in the edge environment, close to the
        user and designed for short tasks.

So in middleware you verify at most a signed token – Web Crypto can do that. Anything needing the database happens afterwards.

Rewriting instead of redirecting

rewrite.ts
// redirect: the address visibly changes
return NextResponse.redirect(new URL("/sign-in", request.url));

// rewrite: the address stays, something else is rendered internally
return NextResponse.rewrite(new URL("/en" + path, request.url));
Rewriting is the usual route for multilingual sites or multi-tenancy.
In more depth: deriving the language from the request optional
middleware.ts
const LANGS = ["en", "de"];

export function middleware(request: NextRequest) {
  const path = request.nextUrl.pathname;

  // Already a language in the address? Then do nothing.
  if (LANGS.some((l) => path.startsWith("/" + l + "/") || path === "/" + l)) {
    return NextResponse.next();
  }

  const wanted = request.headers.get("accept-language") ?? "";
  const lang = wanted.startsWith("de") ? "de" : "en";

  return NextResponse.redirect(new URL("/" + lang + path, request.url));
}

export const config = {
  matcher: ["/((?!_next|api|.*\\.).*)"],   // no assets, no API
};
Tip Redirect once, then remember

Someone who actively switches language does not want to be redirected again on their next visit. The usual approach is to store the choice in a cookie and check that before the header in the middleware.

Does it stick?

4 questions on this lesson. Wrong answers show up in your stats.

Hirefullstack

Need React firepower on your team?

We have been building React and Next.js applications for clients across Germany for years – as a single expert, as reinforcement for an existing team, or as a complete Scrum team.

Talk about your project →