Next.js cookbook · Intermediate

Protect routes in the Next.js App Router

Redirect signed-out users away from /dashboard and make sure server code never serves private data to them.

Approach

Use the request interceptor (middleware.ts, renamed proxy.ts in Next.js 16) for a fast, optimistic redirect based on the session cookie — but always verify the session again where data is read (Server Components, Server Actions, Route Handlers), typically in a cached verifySession() data-access helper. The interceptor alone is not a security boundary.

Solution

// proxy.ts (Next.js 16; use middleware.ts + `export function middleware` on 15)
import { NextResponse, type NextRequest } from "next/server";

export function proxy(request: NextRequest) {
  const hasSession = request.cookies.has("session");
  if (!hasSession) {
    const url = new URL("/login", request.url);
    url.searchParams.set("next", request.nextUrl.pathname);
    return NextResponse.redirect(url);
  }
  return NextResponse.next();
}
export const config = { matcher: ["/dashboard/:path*", "/settings/:path*"] };

// lib/dal.ts — the real check, close to the data
import "server-only";
import { cache } from "react";
import { cookies } from "next/headers";
import { redirect } from "next/navigation";

export const verifySession = cache(async () => {
  const token = (await cookies()).get("session")?.value;
  const session = token ? await decryptSession(token) : null;
  if (!session?.userId) redirect("/login");
  return session;
});

// app/dashboard/page.tsx
export default async function Dashboard() {
  const session = await verifySession();
  const projects = await db.project.findMany({ where: { ownerId: session.userId } });
  return <ProjectList projects={projects} />;
}

Pitfalls

  • Relying only on middleware/proxy for authorization.
  • Checking auth in a layout and assuming child pages are protected (layouts don't re-render on every navigation).
  • Open redirects via the next parameter — only allow relative paths.

Variations

  • Auth.js (auth()), Clerk or Better Auth provide sessions and helpers.
  • Check roles in the DAL for admin-only data.

Further reading: Next.js: Authentication guide

Reference: https://nextjs.org/docs/app/guides/authentication

More Next.js recipes