How to Use Middleware for Authentication in Next.js
In Next.js, use the
middleware file to run code before requests reach your pages or API routes. You can check authentication by reading cookies or headers inside middleware.ts and redirect users if they are not logged in.Syntax
The middleware.ts file exports a middleware function that receives a NextRequest object and returns a NextResponse. You can inspect the request, check cookies or headers for auth tokens, and decide to continue or redirect.
Use NextResponse.next() to continue, or NextResponse.redirect() to send users elsewhere.
typescript
import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; export function middleware(request: NextRequest) { // Read cookie or header const token = request.cookies.get('token')?.value; if (!token) { // Redirect to login if no token return NextResponse.redirect(new URL('/login', request.url)); } // Continue if token exists return NextResponse.next(); } export const config = { matcher: ['/protected/:path*'], };
Example
This example shows middleware protecting all routes under /protected. If the user has no token cookie, they are redirected to /login. Otherwise, the request continues.
typescript
import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; export function middleware(request: NextRequest) { const token = request.cookies.get('token')?.value; if (!token) { return NextResponse.redirect(new URL('/login', request.url)); } return NextResponse.next(); } export const config = { matcher: ['/protected/:path*'], };
Output
When accessing /protected/dashboard without a 'token' cookie, the user is redirected to /login. With the cookie, the page loads normally.
Common Pitfalls
- Not specifying
matcherinconfigcauses middleware to run on all routes, which may slow your app. - Forgetting to check for cookie existence can cause errors.
- Using client-side redirects instead of middleware redirects delays protection.
- Not handling public routes properly can block access unintentionally.
typescript
/* Wrong: Middleware runs everywhere and blocks all users */ import { NextResponse } from 'next/server'; export function middleware(request) { const token = request.cookies.get('token')?.value; if (!token) { return NextResponse.redirect(new URL('/login', request.url)); } return NextResponse.next(); } // No matcher config, so runs on all routes /* Right: Use matcher to limit middleware to protected routes */ export const config = { matcher: ['/protected/:path*'], };
Quick Reference
- middleware.ts: File where middleware logic lives.
- NextRequest: Object representing the incoming request.
- NextResponse.next(): Continue request normally.
- NextResponse.redirect(url): Redirect user to another page.
- config.matcher: Specify which routes middleware applies to.
Key Takeaways
Use middleware.ts to run code before protected routes load in Next.js.
Check authentication tokens in cookies or headers inside middleware.
Redirect unauthenticated users with NextResponse.redirect to login.
Limit middleware scope with config.matcher to avoid slowing your app.
Always test middleware behavior on both protected and public routes.