Lang x Lang

Middleware

Middleware は、request が完了する前に code を実行できるようにします。そして、受信した request に基づいて、書き換えたり、Redirect したり、request や response headers を変更したり、直接応答したりすることで、response を変更できます。

Middleware はキャッシュされたコンテンツと routes がマッチする前に実行されます。詳細は Paths のマッチングをご覧ください。

Convention

あなたのプロジェクトの root で middleware.ts ファイル(または .js)を使って、Middleware を定義します。例えば、pages または app と同じレベル、または該当する場合は src の中にあります。

Example

middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

// This function can be marked `async` if using `await` inside
export function middleware(request: NextRequest) {
  return NextResponse.redirect(new URL('/home', request.url))
}

// See "Matching Paths" below to learn more
export const config = {
  matcher: '/about/:path*',
}
middleware.js
import { NextResponse } from 'next/server'

// This function can be marked `async` if using `await` inside
export function middleware(request) {
  return NextResponse.redirect(new URL('/home', request.url))
}

// See "Matching Paths" below to learn more
export const config = {
  matcher: '/about/:path*',
}

Matching Paths

Middleware は、プロジェクト内のすべての routeで呼び出されます。以下は実行順序です:

  1. next.config.jsからのheaders
  2. next.config.jsからのredirects
  3. Middleware ( rewritesredirectsなど)
  4. next.config.jsからのbeforeFiles(rewrites)
  5. ファイルシステム routes (public/_next/static/pages/app/、等)
  6. next.config.jsからのafterFiles(rewrites)
  7. Dynamic Routes (/blog/[slug])
  8. next.config.jsからのfallback(rewrites)

paths Middleware が実行される path を定義する方法は二つあります:

  1. カスタム matcher config
  2. 条件文

Matcher

matcherは、特定の paths で実行するための Middleware をフィルタリングできます。

middleware.js
export const config = {
  matcher: '/about/:path*',
}

あなたは単一の path または複数の paths を配列 syntax で一致させることができます:

middleware.js
export const config = {
  matcher: ['/about/:path*', '/dashboard/:path*'],
}

matcher config は完全な正規表現を許可するので、否定的先読みや文字のマッチングなどのマッチングがサポートされています。特定の paths を除いてすべてをマッチさせる否定的先読みの例はここで見ることができます:

middleware.js
export const config = {
  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 (favicon file)
     */
    '/((?!api|_next/static|_next/image|favicon.ico).*)',
  ],
}

あなたはまた、missing配列を使用してnext/linkからの事前フェッチ (prefetches)を無視できます。これは Middleware を通過する必要がないものです:

middleware.js
export const config = {
  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 (favicon file)
     */
    {
      source: '/((?!api|_next/static|_next/image|favicon.ico).*)',
      missing: [
        { type: 'header', key: 'next-router-prefetch' },
        { type: 'header', key: 'purpose', value: 'prefetch' },
      ],
    },
  ],
}

Good to knowmatcherの値は build 時に静的に解析できるように定数である必要があります。 Dynamic のような変数などの値は無視されます。

設定されたマッチャー:

  1. MUST start は/で始まるべきです。
  2. 名前付きパラメータを含めることができます: /about/:path/about/a/about/bに一致しますが、/about/a/cには一致しません。
  3. 名前付きパラメータ(:で始まる)には修飾子を付けることができます:/about/:path**ゼロ以上であるため、/about/a/b/cに一致します。?ゼロまたは 1で、+1 以上です。
  4. 括弧で囲まれた正規表現を使用することができます: /about/(.*)/about/:path*と同じです。

path-to-regexp のドキュメンテーションで詳細を読んでください。

Good to know: 後方互換性のため、 Next.js は常に /public/public/index と見なします。そのため、/public/:pathの matcher は一致します。

条件文

middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  if (request.nextUrl.pathname.startsWith('/about')) {
    return NextResponse.rewrite(new URL('/about-2', request.url))
  }

  if (request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.rewrite(new URL('/dashboard/user', request.url))
  }
}
middleware.js
import { NextResponse } from 'next/server'

export function middleware(request) {
  if (request.nextUrl.pathname.startsWith('/about')) {
    return NextResponse.rewrite(new URL('/about-2', request.url))
  }

  if (request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.rewrite(new URL('/dashboard/user', request.url))
  }
}

NextResponse

NextResponse API は以下を可能にします:

  • redirectで、受信した request を別の URL に転送します
  • rewrite によって、与えられた URL を表示する形で response を書き換えます
  • getServerSidePropsrewrite の行き先、そして API Routes に対して requestheaders を設定します
  • response cookies を設定する
  • response headers を設定する

Middleware から response を生成するために、以下のことができます:

  1. rewrite を行い、route (Page または Route Handler) を生成し、response を出力します。
  2. NextResponseを直接返します。Response の生成を参照してください。

Using Cookies

Cookies は通常の headers です。Requestでは、それらはCookie header に保存されます。Responseでは、それらは Set-Cookie header にあります。 Next.js は、NextRequestおよびNextResponsecookies拡張を通じてこれらの cookies にアクセスし、操作する便利な方法を提供します。

  1. 受信したリクエストに対して、cookiesには次のメソッドが付属しています:getgetAllsetdelete cookies 。 hasを使用して cookie の存在を確認したり、clearを使用してすべての cookies を削除することができます。
  2. 送信応答のために、cookiesは次の Method getgetAllset、そしてdeleteを持っています。
middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(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: '/' }
  const allCookies = 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
  const response = 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
}
middleware.js
import { NextResponse } from 'next/server'

export function middleware(request) {
  // 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: '/' }
  const allCookies = 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
  const response = 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=/test` header.

  return response
}

Setting Headers

NextResponse API を使用して、request と response headers を設定することができます(request headers の設定は Next.js v13.0.0 から利用可能です)。

middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  // Clone the request headers and set a new header `x-hello-from-middleware1`
  const requestHeaders = new Headers(request.headers)
  requestHeaders.set('x-hello-from-middleware1', 'hello')

  // You can also set request headers in NextResponse.rewrite
  const response = 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
}
middleware.js
import { NextResponse } from 'next/server'

export function middleware(request) {
  // Clone the request headers and set a new header `x-hello-from-middleware1`
  const requestHeaders = new Headers(request.headers)
  requestHeaders.set('x-hello-from-middleware1', 'hello')

  // You can also set request headers in NextResponse.rewrite
  const response = 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: 大きな headers を設定するのは避けてください。それはバックエンドのウェブ server の設定によっては、431 Request Header Fields Too Large error を引き起こす可能性があります。

CORS

Middleware で CORS headers を設定することで、単純 なリクエストや事前に飛ばされる リクエストを含むクロスオリジンリクエストを許可することができます。

middleware.ts
import { NextRequest, NextResponse } from 'next/server'

const allowedOrigins = ['https://acme.com', 'https://my-app.org']

const corsOptions = {
  'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type, Authorization',
}

export function middleware(request: NextRequest) {
  // Check the origin from the request
  const origin = request.headers.get('origin') ?? ''
  const isAllowedOrigin = allowedOrigins.includes(origin)

  // Handle preflighted requests
  const isPreflight = request.method === 'OPTIONS'

  if (isPreflight) {
    const preflightHeaders = {
      ...(isAllowedOrigin && { 'Access-Control-Allow-Origin': origin }),
      ...corsOptions,
    }
    return NextResponse.json({}, { headers: preflightHeaders })
  }

  // Handle simple requests
  const response = 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
}

export const config = {
  matcher: '/api/:path*',
}
middleware.js
import { NextResponse } from 'next/server'

const allowedOrigins = ['https://acme.com', 'https://my-app.org']

const corsOptions = {
  'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type, Authorization',
}

export function middleware(request) {
  // Check the origin from the request
  const origin = request.headers.get('origin') ?? ''
  const isAllowedOrigin = allowedOrigins.includes(origin)

  // Handle preflighted requests
  const isPreflight = request.method === 'OPTIONS'

  if (isPreflight) {
    const preflightHeaders = {
      ...(isAllowedOrigin && { 'Access-Control-Allow-Origin': origin }),
      ...corsOptions,
    }
    return NextResponse.json({}, { headers: preflightHeaders })
  }

  // Handle simple requests
  const response = 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
}

export const config = {
  matcher: '/api/:path*',
}

Good to know: 個々の routes に対して CORS headers を Route ハンドラで設定することができます。

Producing a Response

Response または NextResponse インスタンスを返すことで、Middleware から直接応答することができます。(これはNext.js v13.1.0 から利用可能です)

middleware.ts
import { NextRequest } from 'next/server'
import { isAuthenticated } from '@lib/auth'

// Limit the middleware to paths starting with `/api/`
export const config = {
  matcher: '/api/:function*',
}

export function middleware(request: NextRequest) {
  // Call our authentication function to check the request
  if (!isAuthenticated(request)) {
    // Respond with JSON indicating an error message
    return Response.json(
      { success: false, message: 'authentication failed' },
      { status: 401 }
    )
  }
}
middleware.js
import { isAuthenticated } from '@lib/auth'

// Limit the middleware to paths starting with `/api/`
export const config = {
  matcher: '/api/:function*',
}

export function middleware(request) {
  // Call our authentication function to check the request
  if (!isAuthenticated(request)) {
    // Respond with JSON indicating an error message
    return Response.json(
      { success: false, message: 'authentication failed' },
      { status: 401 }
    )
  }
}

waitUntilNextFetchEvent

NextFetchEvent object は、ネイティブの FetchEvent object を拡張し、waitUntil() method を含んでいます。

waitUntil() の method は引数として promise を取り、promise がセトルするまでの Middleware の寿命を延ばします。これはバックグラウンドでの作業を実行するのに役立ちます。

middleware.ts
import { NextResponse } from 'next/server'
import type { NextFetchEvent, NextRequest } from 'next/server'

export function middleware(req: NextRequest, event: NextFetchEvent) {
  event.waitUntil(
    fetch('https://my-analytics-platform.com', {
      method: 'POST',
      body: JSON.stringify({ pathname: req.nextUrl.pathname }),
    })
  )

  return NextResponse.next()
}

Advanced Middleware Flags

v13.1 の Next.js では、middleware 用に 2 つの追加フラグ、skipMiddlewareUrlNormalizeskipTrailingSlashRedirect が導入され、高度な使用例を処理することが可能になりました。

skipTrailingSlashRedirect は、トレーリングスラッシュの追加や削除のための Next.js redirects を無効にします。これにより、 middleware 内でカスタム処理を許可し、一部の paths ではトレーリングスラッシュを維持し、他の一部では維持しないことが可能になります。これにより、インクリメンタルな移行が容易になります。

next.config.js
module.exports = {
  skipTrailingSlashRedirect: true,
}
middleware.js
const legacyPrefixes = ['/docs', '/blog']

export default async function middleware(req) {
  const { pathname } = req.nextUrl

  if (legacyPrefixes.some((prefix) => pathname.startsWith(prefix))) {
    return NextResponse.next()
  }

  // apply trailing slash handling
  if (
    !pathname.endsWith('/') &&
    !pathname.match(/((?!\.well-known(?:\/.*)?)(?:[^/]+\/)*[^/]+\.\w+)/)
  ) {
    req.nextUrl.pathname += '/'
    return NextResponse.redirect(req.nextUrl)
  }
}

skipMiddlewareUrlNormalize は、Next.js における URL 正規化を無効にして、直接の訪問と Client 遷移の処理を同じにすることを可能にします。いくつかの高度なケースでは、このオプションにより、オリジナルの URL を使用して全体的なコントロールが可能になります。

next.config.js
module.exports = {
  skipMiddlewareUrlNormalize: true,
}
middleware.js
export default async function middleware(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
}

Runtime

現在、Middleware は Edge runtime のみをサポートしています。Node.js runtime は使用できません。

Version History

Version変更点
v13.1.0高度な Middleware フラグが追加されました
v13.0.0Middleware は requestheaders、responses headers を変更し、responses を送信できます
v12.2.0Middleware は安定しています。アップグレードガイドをご覧ください
v12.0.9絶対 URL を Edge Runtime で強制する (PR )
v12.0.0Middleware (ベータ版) 追加されました

当社サイトでは、Cookie を使用しています。各規約をご確認の上ご利用ください:
Cookie Policy, Privacy Policy および Terms of Use