Next.js
Next.js Middleware: When to Use It (And When Not To)
Redirects, rewrites, auth checks, and locale—when middleware is the right tool and when to use something else.

Next.js middleware runs at the edge before a request is completed. It’s great for a few jobs and wrong for others. Use it where it shines and avoid it for heavy or stateful logic.
What middleware is good for
Middleware runs before the request hits your route. Use it for: redirects (e.g. old URL to new, HTTP to HTTPS, www to non-www), simple rewrites (e.g. A/B or feature flags by path), and lightweight auth checks (e.g. redirect unauthenticated users to login by inspecting a cookie or header). Also use it for setting headers (e.g. security headers, locale) or selecting locale in i18n setups. Keep the logic fast and stateless; the edge has limits on run time and memory.
// middleware.ts at project root
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) {
const token = request.cookies.get("session");
if (!token && request.nextUrl.pathname.startsWith("/dashboard")) {
return NextResponse.redirect(new URL("/login", request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ["/dashboard/:path*", "/account/:path*"],
};Use the matcher config so middleware runs only on the routes that need it; that keeps the rest of the app unaffected.
When not to use middleware
Don’t do heavy work: no DB calls, no big computations, no calls to slow APIs. Don’t rely on middleware for full authorization (e.g. fine-grained permissions); do that in the route or in a server component/action after you have the user context. Don’t put business logic that belongs in the app (e.g. complex feature flags or pricing logic) in middleware—use it for routing and simple checks only. If you need to read the body or stream the response, middleware isn’t the place; handle that in route handlers or server components.
Redirects and rewrites
For static redirects (e.g. /old → /new), you can use next.config.js redirects instead of middleware; that’s simpler and runs at the same layer. Use middleware when the redirect depends on the request (cookie, header, geo). Same for rewrites: config-based rewrites for fixed rules; middleware when the target depends on the request.
Summary
Use middleware for fast, stateless work: redirects by request, simple auth redirects, locale, and headers. Keep it thin; do authorization and business logic in the app. Use the matcher to limit scope. That way middleware stays useful without becoming a bottleneck or a maintenance trap.