Routing: Middleware | Next.js

Middleware allows you to run code before a request is completed. Then, based on the incoming request, you can modify the response by rewriting, redirecting, modifying the request or response headers, or responding directly.

Middleware runs before cached content and routes are matched. See Matching Paths for more details.

Integrating Middleware into your application can lead to significant improvements in performance, security, and user experience. Some common scenarios where Middleware is particularly effective include:

  • Authentication and Authorization: Ensure user identity and check session cookies before granting access to specific pages or API routes.
  • Server-Side Redirects: Redirect users at the server level based on certain conditions (e.g., locale, user role).
  • Path Rewriting: Support A/B testing, feature rollouts, or legacy paths by dynamically rewriting paths to API routes or pages based on request properties.
  • Bot Detection: Protect your resources by detecting and blocking bot traffic.
  • Logging and Analytics: Capture and analyze request data for insights before processing by the page or API.
  • Feature Flagging: Enable or disable features dynamically for seamless feature rollouts or testing.

Recognizing situations where middleware may not be the optimal approach is just as crucial. Here are some scenarios to be mindful of:

  • Complex Data Fetching and Manipulation: Middleware is not designed for direct data fetching or manipulation, this should be done within Route Handlers or server-side utilities instead.
  • Heavy Computational Tasks: Middleware should be lightweight and respond quickly or it can cause delays in page load. Heavy computational tasks or long-running processes should be done within dedicated Route Handlers.
  • Extensive Session Management: While Middleware can manage basic session tasks, extensive session management should be managed by dedicated authentication services or within Route Handlers.
  • Direct Database Operations: Performing direct database operations within Middleware is not recommended. Database interactions should done within Route Handlers or server-side utilities.

Use the file middleware.ts (or .js) in the root of your project to define Middleware. For example, at the same level as pages or app, or inside src if applicable.

Note: While only one middleware.ts file is supported per project, you can still organize your middleware logic modularly. Break out middleware functionalities into separate .ts or .js files and import them into your main middleware.ts file. This allows for cleaner management of route-specific middleware, aggregated in the middleware.ts for centralized control. By enforcing a single middleware file, it simplifies configuration, prevents potential conflicts, and optimizes performance by avoiding multiple middleware layers.

import { NextResponse } from'next/server'
importtype { NextRequest } from'next/server'
// This function can be marked `async` if using `await` inside
exportfunctionmiddleware(request:NextRequest) {
returnNextResponse.redirect(newURL('/home',request.url))
}
// See "Matching Paths" below to learn more
exportconstconfig= {
  matcher:'/about/:path*',
}

Middleware will be invoked for every route in your project. Given this, it’s crucial to use matchers to precisely target or exclude specific routes. The following is the execution order:

  1. headers from next.config.js
  2. redirects from next.config.js
  3. Middleware (rewrites, redirects, etc.)
  4. beforeFiles (rewrites) from next.config.js
  5. Filesystem routes (public/, _next/static/, pages/, app/, etc.)
  6. afterFiles (rewrites) from next.config.js
  7. Dynamic Routes (/blog/[slug])
  8. fallback (rewrites) from next.config.js

There are two ways to define which paths Middleware will run on:

  1. Custom matcher config
  2. Conditional statements

Matcher

matcher allows you to filter Middleware to run on specific paths.

exportconstconfig= {
  matcher:'/about/:path*',
}

You can match a single path or multiple paths with an array syntax:

exportconstconfig= {
  matcher: ['/about/:path*','/dashboard/:path*'],
}

The matcher config allows full regex so matching like negative lookaheads or character matching is supported. An example of a negative lookahead to match all except specific paths can be seen here:

exportconstconfig= {
  matcher: [
/*
     * Match all request paths except for the ones starting with:
     * - api (API routes)
     * - _next/static (static files)
     * - _next/image (image optimization files)
     * - favicon.ico, sitemap.xml, robots.txt (metadata files)
     */
'/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
  ],
}

You can also bypass Middleware for certain requests by using the missing or has arrays, or a combination of both:

exportconstconfig= {
  matcher: [
/*
     * Match all request paths except for the ones starting with:
     * - api (API routes)
     * - _next/static (static files)
     * - _next/image (image optimization files)
     * - favicon.ico, sitemap.xml, robots.txt (metadata files)
     */
    {
      source:
'/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
      missing: [
        { type:'header', key:'next-router-prefetch' },
        { type:'header', key:'purpose', value:'prefetch' },
      ],
    },
    {
      source:
'/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
      has: [
        { type:'header', key:'next-router-prefetch' },
        { type:'header', key:'purpose', value:'prefetch' },
      ],
    },
    {
      source:
'/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
      has: [{ type:'header', key:'x-present' }],
      missing: [{ type:'header', key:'x-missing', value:'prefetch' }],
    },
  ],
}

Good to know: The matcher values need to be constants so they can be statically analyzed at build-time. Dynamic values such as variables will be ignored.

Configured matchers:

  1. MUST start with /
  2. Can include named parameters: /about/:path matches /about/a and /about/b but not /about/a/c
  3. Can have modifiers on named parameters (starting with :): /about/:path* matches /about/a/b/c because * is zero or more. ? is zero or one and + one or more
  4. Can use regular expression enclosed in parenthesis: /about/(.*) is the same as /about/:path*

Read more details on path-to-regexp documentation.

Good to know: For backward compatibility, Next.js always considers /public as /public/index. Therefore, a matcher of /public/:path will match.

Conditional Statements

import { NextResponse } from'next/server'
importtype { NextRequest } from'next/server'
exportfunctionmiddleware(request:NextRequest) {
if (request.nextUrl.pathname.startsWith('/about')) {
returnNextResponse.rewrite(newURL('/about-2',request.url))
  }
if (request.nextUrl.pathname.startsWith('/dashboard')) {
returnNextResponse.rewrite(newURL('/dashboard/user',request.url))
  }
}

The NextResponse API allows you to:

  • redirect the incoming request to a different URL
  • rewrite the response by displaying a given URL
  • Set request headers for API Routes, getServerSideProps, and rewrite destinations
  • Set response cookies
  • Set response headers

To produce a response from Middleware, you can:

  1. rewrite to a route (Page or Route Handler) that produces a response
  2. return a NextResponse directly. See Producing a Response

Cookies are regular headers. On a Request, they are stored in the Cookie header. On a Response they are in the Set-Cookie header. Next.js provides a convenient way to access and manipulate these cookies through the cookies extension on NextRequest and NextResponse.

  1. For incoming requests, cookies comes with the following methods: get, getAll, set, and delete cookies. You can check for the existence of a cookie with has or remove all cookies with clear.
  2. For outgoing responses, cookies have the following methods get, getAll, set, and delete.
import { NextResponse } from'next/server'
importtype { NextRequest } from'next/server'
exportfunctionmiddleware(request:NextRequest) {
// Assume a "Cookie:nextjs=fast" header to be present on the incoming request
// Getting cookies from the request using the `RequestCookies` API
let cookie =request.cookies.get('nextjs')
console.log(cookie) // => { name: 'nextjs', value: 'fast', Path: '/' }
constallCookies=request.cookies.getAll()
console.log(allCookies) // => [{ name: 'nextjs', value: 'fast' }]
request.cookies.has('nextjs') // => true
request.cookies.delete('nextjs')
request.cookies.has('nextjs') // => false
// Setting cookies on the response using the `ResponseCookies` API
constresponse=NextResponse.next()
response.cookies.set('vercel','fast')
response.cookies.set({
    name:'vercel',
    value:'fast',
    path:'/',
  })
  cookie =response.cookies.get('vercel')
console.log(cookie) // => { name: 'vercel', value: 'fast', Path: '/' }
// The outgoing response will have a `Set-Cookie:vercel=fast;path=/` header.
return response
}

You can set request and response headers using the NextResponse API (setting request headers is available since Next.js v13.0.0).

import { NextResponse } from'next/server'
importtype { NextRequest } from'next/server'
exportfunctionmiddleware(request:NextRequest) {
// Clone the request headers and set a new header `x-hello-from-middleware1`
constrequestHeaders=newHeaders(request.headers)
requestHeaders.set('x-hello-from-middleware1','hello')
// You can also set request headers in NextResponse.next
constresponse=NextResponse.next({
    request: {
// New request headers
      headers: requestHeaders,
    },
  })
// Set a new response header `x-hello-from-middleware2`
response.headers.set('x-hello-from-middleware2','hello')
return response
}

Good to know: Avoid setting large headers as it might cause 431 Request Header Fields Too Large error depending on your backend web server configuration.

CORS

You can set CORS headers in Middleware to allow cross-origin requests, including simple and preflighted requests.

import { NextRequest, NextResponse } from'next/server'
constallowedOrigins= ['https://acme.com','https://my-app.org']
constcorsOptions= {
'Access-Control-Allow-Methods':'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers':'Content-Type, Authorization',
}
exportfunctionmiddleware(request:NextRequest) {
// Check the origin from the request
constorigin=request.headers.get('origin') ??''
constisAllowedOrigin=allowedOrigins.includes(origin)
// Handle preflighted requests
constisPreflight=request.method ==='OPTIONS'
if (isPreflight) {
constpreflightHeaders= {
...(isAllowedOrigin && { 'Access-Control-Allow-Origin': origin }),
...corsOptions,
    }
returnNextResponse.json({}, { headers: preflightHeaders })
  }
// Handle simple requests
constresponse=NextResponse.next()
if (isAllowedOrigin) {
response.headers.set('Access-Control-Allow-Origin', origin)
  }
Object.entries(corsOptions).forEach(([key, value]) => {
response.headers.set(key, value)
  })
return response
}
exportconstconfig= {
  matcher:'/api/:path*',
}

Good to know: You can configure CORS headers for individual routes in Route Handlers.

You can respond from Middleware directly by returning a Response or NextResponse instance. (This is available since Next.js v13.1.0)

importtype { NextRequest } from'next/server'
import { isAuthenticated } from'@lib/auth'
// Limit the middleware to paths starting with `/api/`
exportconstconfig= {
  matcher:'/api/:function*',
}
exportfunctionmiddleware(request:NextRequest) {
// Call our authentication function to check the request
if (!isAuthenticated(request)) {
// Respond with JSON indicating an error message
returnResponse.json(
      { success:false, message:'authentication failed' },
      { status:401 }
    )
  }
}

waitUntil and NextFetchEvent

The NextFetchEvent object extends the native FetchEvent object, and includes the waitUntil() method.

The waitUntil() method takes a promise as an argument, and extends the lifetime of the Middleware until the promise settles. This is useful for performing work in the background.

import { NextResponse } from'next/server'
importtype { NextFetchEvent, NextRequest } from'next/server'
exportfunctionmiddleware(req:NextRequest, event:NextFetchEvent) {
event.waitUntil(
fetch('https://my-analytics-platform.com', {
      method:'POST',
      body:JSON.stringify({ pathname:req.nextUrl.pathname }),
    })
  )
returnNextResponse.next()
}

In v13.1 of Next.js two additional flags were introduced for middleware, skipMiddlewareUrlNormalize and skipTrailingSlashRedirect to handle advanced use cases.

skipTrailingSlashRedirect disables Next.js redirects for adding or removing trailing slashes. This allows custom handling inside middleware to maintain the trailing slash for some paths but not others, which can make incremental migrations easier.

module.exports= {
  skipTrailingSlashRedirect:true,
}
constlegacyPrefixes= ['/docs','/blog']
exportdefaultasyncfunctionmiddleware(req) {
const { pathname } =req.nextUrl
if (legacyPrefixes.some((prefix) =>pathname.startsWith(prefix))) {
returnNextResponse.next()
  }
// apply trailing slash handling
if (
!pathname.endsWith('/') &&
!pathname.match(/((?!\.well-known(?:\/.*)?)(?:[^/]+\/)*[^/]+\.\w+)/)
  ) {
returnNextResponse.redirect(
newURL(`${req.nextUrl.pathname}/`,req.nextUrl)
    )
  }
}

skipMiddlewareUrlNormalize allows for disabling the URL normalization in Next.js to make handling direct visits and client-transitions the same. In some advanced cases, this option provides full control by using the original URL.

module.exports= {
  skipMiddlewareUrlNormalize:true,
}
exportdefaultasyncfunctionmiddleware(req) {
const { pathname } =req.nextUrl
// GET /_next/data/build-id/hello.json
console.log(pathname)
// with the flag this now /_next/data/build-id/hello.json
// without the flag this would be normalized to /hello
}

Middleware currently only supports APIs compatible with the Edge runtime. APIs exclusive to Node.js are unsupported.

Version Changes
v13.1.0 Advanced Middleware flags added
v13.0.0 Middleware can modify request headers, response headers, and send responses
v12.2.0 Middleware is stable, please see the upgrade guide
v12.0.9 Enforce absolute URLs in Edge Runtime (PR)
v12.0.0 Middleware (Beta) added

Related Posts

Thủ tướng Israel: Iran đã phạm sai lầm lớn và sẽ trả giá

Thủ tướng Netanyahu cảnh báo Iran phạm “sai lầm nghiêm trọng” và sẽ trả giá vì đã phóng tên lửa vào Israel, trong khi Tehran tuyên bố thực thi quyền tự vệ. “Iran không hiểu được quyết tâm tự vệ và đáp trả mọi kẻ thù của Israel. Thủ lĩnh Yahya Sinwar và …

Read more

Justin Bieber Speaks Out on Usher’s Betrayal and Diddy’s Abuse

Justin Bieber’s Relationship with Diddy and Usher: A Journey into a Troubling Past. In recent times, Justin Bieber has been making headlines again, particularly following the federal raids on Sean “Diddy” Combs’ properties in connection with an ongoing …

Read more

The Reаl Hulk. Photogrаphed In Afrіca In The Yeаr 1944.

In a captivating glimpse into the past, a remarkable photograph from 1944 Africa has resurfaced, unveiling what some have playfully dubbed “The Real Hulk.” This extraordinary image, taken during a…

Read more

Federal Evidence Unveils the Truth Behind Diddy’s Adopted Daughter’s Mysterious Case

Creepy Federal Evidence Reveals What Happened To Diddy’s Adopted Daughter | F0rced Into Fr3ak0ffs? Y’all, the feds just dropped more information on Diddy’s crim3s, and this particular dump allegedly involves information on Diddy’s adopted daughter, Ava …

Read more

Fact or Fictioп? The Thrilliпg Rυmors of Alieп Existeпce That Have Left Maпy Terrified aпd oп Edge!

The Roswell UFO iпcideпt remaiпs oпe of the most captivatiпg aпd coпtroversial eveпts iп the aппals of moderп history. The alleged crash of a mysterioυs aircraft пear Roswell, New Mexico…

Read more

Diddy’s Ex-Bodyguard Leaks Scandalous Photos Involving Justin Bieber and Diddy

Diddy’s Ex Bodyguard RELEASES New Gay Pictures HUMILIATING Diddy In recent news, Diddy’s ex-bodyguard has released a set of scandalous photos that have shocked the internet. The pictures, which show Diddy in compromising positions with other men, have …

Read more

Leave a Reply

Your email address will not be published. Required fields are marked *