Loading...
Loading...
Middleware in Next.js is a function that runs before every request is processed. It lets you intercept requests and modify the response before it reaches the user.
Middleware is defined in a middleware.ts file at the project root (next to app/ or pages/):
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl
if (pathname.startsWith('/admin')) {
const token = request.cookies.get('session')
if (!token) {
return NextResponse.redirect(new URL('/auth/login', request.url))
}
}
return NextResponse.next()
}
export const config = {
matcher: ['/admin/:path*', '/settings/:path*']
}
config.matcher defines which paths trigger the middleware:
export const config = {
matcher: [
'/admin/:path*',
'/settings/:path*',
'/((?!api|_next/static|_next/image|favicon.ico).*)'
]
}
Without a matcher, middleware runs for every request including static files. That is unnecessary overhead.
export function middleware(request: NextRequest) {
const token = request.cookies.get('session')
const isAuthPage = request.nextUrl.pathname.startsWith('/auth')
if (!token && !isAuthPage) {
return NextResponse.redirect(new URL('/auth/login', request.url))
}
if (token && isAuthPage) {
return NextResponse.redirect(new URL('/dashboard', request.url))
}
return NextResponse.next()
}
This is how localization works on Hack Frontend: middleware detects the user's language and redirects to the corresponding path.
const locales = ['ru', 'en']
const defaultLocale = 'ru'
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl
const hasLocale = locales.some(
locale => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
)
if (hasLocale) return NextResponse.next()
const locale = request.headers.get('accept-language')?.startsWith('en')
? 'en'
: defaultLocale
return NextResponse.redirect(
new URL(`/${locale}${pathname}`, request.url)
)
}
export function middleware(request: NextRequest) {
const response = NextResponse.next()
response.headers.set('x-request-id', crypto.randomUUID())
response.headers.set(
'Content-Security-Policy',
"default-src 'self'"
)
return response
}
Middleware runs on Edge Runtime, not Node.js. This means:
Limitations:
You cannot use Prisma, heavy npm packages or access the database directly in Middleware. For complex authorization logic it is better to verify the token in middleware and perform detailed permission checks in server components or Server Actions.
Middleware can perform multiple actions sequentially:
export function middleware(request: NextRequest) {
const response = NextResponse.next()
// 1. Logging
console.log(`${request.method} ${request.nextUrl.pathname}`)
// 2. Adding a header
response.headers.set('x-pathname', request.nextUrl.pathname)
// 3. A/B testing
if (!request.cookies.get('ab-variant')) {
response.cookies.set('ab-variant', Math.random() > 0.5 ? 'A' : 'B')
}
return response
}