コンテンツへスキップ

ミドルウェア

ミドルウェアを使用すると、リクエストが完了する前にコードを実行できます。その後、受信したリクエストに基づいて、書き換え、リダイレクト、リクエストまたはレスポンスヘッダーの変更、または直接応答によってレスポンスを変更できます。

ミドルウェアはキャッシュされたコンテンツやルートがマッチされる前に実行されます。詳細については、パスのマッチングを参照してください。

ユースケース

ミドルウェアが効果的な一般的なシナリオには、以下のようなものがあります。

  • 受信リクエストの一部を読み取った後の迅速なリダイレクト
  • A/Bテストや実験に基づいて異なるページに書き換え
  • すべてのページまたは一部のページのヘッダーの変更

ミドルウェアが適さないのは、以下の目的の場合です。

  • 遅いデータフェッチ
  • セッション管理

規約

プロジェクトのルート (例: pages または app と同じレベル、または該当する場合は src 内) にある middleware.ts (または .js) ファイルを使用してミドルウェアを定義します。

注意: プロジェクトごとにサポートされる middleware.ts ファイルは1つだけですが、ミドルウェアロジックをモジュール式に整理することは可能です。ミドルウェア機能を個別の.tsまたは.jsファイルに分割し、それらをメインのmiddleware.tsファイルにインポートしてください。これにより、ルート固有のミドルウェアの管理がよりクリーンになり、一元的な制御のためにmiddleware.tsに集約されます。単一のミドルウェアファイルを強制することで、設定が簡素化され、潜在的な競合が防止され、複数のミドルウェアレイヤーを回避することでパフォーマンスが最適化されます。

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*',
}

パスのマッチング

ミドルウェアはプロジェクト内のすべてのルートで呼び出されます。そのため、特定のルートを正確にターゲットにしたり除外したりするために、マッチャーを使用することが重要です。実行順序は次のとおりです。

  1. next.config.jsからのheaders
  2. next.config.jsからのredirects
  3. ミドルウェア (rewritesredirectsなど)
  4. next.config.jsからのbeforeFiles (rewrites)
  5. ファイルシステムルート (public/_next/static/pages/app/など)
  6. next.config.jsからのafterFiles (rewrites)
  7. 動的ルーティング (/blog/[slug])
  8. next.config.jsからのfallback (rewrites)

ミドルウェアが実行されるパスを定義するには、2つの方法があります。

  1. カスタムマッチャー設定
  2. 条件文

マッチャー

matcherを使用すると、ミドルウェアを特定のパスでのみ実行するようにフィルタリングできます。

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

単一のパスまたは複数のパスを配列構文でマッチングできます。

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

matcher設定は完全な正規表現をサポートしており、否定先読みや文字マッチングのようなマッチングが可能です。特定のパスを除くすべてをマッチさせる否定先読みの例は、こちらで確認できます。

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, sitemap.xml, robots.txt (metadata files)
     */
    '/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
  ],
}

missingまたはhas配列、あるいはその両方を組み合わせて使用することで、特定の要求に対してミドルウェアをバイパスすることもできます。

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, 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' }],
    },
  ],
}

補足: matcherの値は、ビルド時に静的に分析できるように定数である必要があります。変数などの動的な値は無視されます。

設定されたマッチャー

  1. /で始まる必要があります。
  2. 名前付きパラメーターを含めることができます: /about/:path/about/a および /about/b とマッチしますが、 /about/a/c とはマッチしません。
  3. 名前付きパラメーター ( : で始まる) に修飾子を付けることができます: /about/:path**0個以上 を意味するため /about/a/b/c とマッチします。 ?0個または1個+1個以上 を意味します。
  4. 括弧で囲まれた正規表現を使用できます: /about/(.*)/about/:path* と同じです。

path-to-regexpのドキュメントで詳細をご確認ください。

補足: 後方互換性のため、Next.js は常に /public/public/index とみなします。したがって、/public/:path のマッチャーはマッチします。

条件文

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))
  }
}

NextResponse

NextResponse APIを使用すると、次のことができます。

  • 受信したリクエストを別のURLにredirect (リダイレクト) する
  • 指定されたURLを表示することでレスポンスをrewrite (書き換え) する
  • APIルート、getServerSideProps、およびrewriteの宛先のリクエストヘッダーを設定する
  • レスポンスクッキーを設定する
  • レスポンスヘッダーを設定する

ミドルウェアからレスポンスを生成するには、次のいずれかの方法があります。

  1. レスポンスを生成するルート (ページまたはルートハンドラー) にrewrite (書き換え) する
  2. NextResponseを直接返します。レスポンスの生成を参照してください。

クッキーの使用

クッキーは通常のヘッダーです。RequestではCookieヘッダーに、ResponseではSet-Cookieヘッダーに格納されます。Next.jsは、NextRequestおよびNextResponsecookies拡張を通じてこれらのクッキーにアクセスし、操作する便利な方法を提供します。

  1. 受信リクエストの場合、cookiesにはgetgetAllsetdeleteのメソッドがあります。hasでクッキーの存在を確認したり、clearですべてのクッキーを削除したりできます。
  2. 送信レスポンスの場合、cookiesにはgetgetAllsetdeleteのメソッドがあります。
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
}

ヘッダーの設定

NextResponse APIを使用して、リクエストおよびレスポンスヘッダーを設定できます (*リクエスト*ヘッダーの設定は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.next
  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
}

補足: バックエンドのウェブサーバー設定によっては431 Request Header Fields Too Largeエラーが発生する可能性があるため、大きなヘッダーを設定することは避けてください。

CORS

ミドルウェアでCORSヘッダーを設定して、シンプルなリクエストプリフライトリクエストを含む、クロスオリジンリクエストを許可することができます。

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*',
}

補足: ルートハンドラーで個別のルートのCORSヘッダーを設定できます。

レスポンスの生成

ResponseまたはNextResponseインスタンスを直接返すことで、ミドルウェアから応答できます。(これはNext.js v13.1.0以降で利用可能です)

middleware.ts
import type { 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 }
    )
  }
}

waitUntilNextFetchEvent

NextFetchEventオブジェクトは、ネイティブのFetchEventオブジェクトを拡張したもので、waitUntil()メソッドを含んでいます。

waitUntil()メソッドは、引数としてPromiseを受け取り、そのPromiseが解決するまでミドルウェアのライフタイムを延長します。これはバックグラウンドで作業を実行するのに役立ちます。

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()
}

高度なミドルウェアフラグ

Next.jsのv13.1では、高度なユースケースを処理するために、ミドルウェア用の2つの追加フラグskipMiddlewareUrlNormalizeskipTrailingSlashRedirectが導入されました。

skipTrailingSlashRedirectは、末尾のスラッシュを追加または削除するためのNext.jsのリダイレクトを無効にします。これにより、ミドルウェア内でカスタム処理を行い、一部のパスでは末尾のスラッシュを維持し、他のパスでは維持しないことが可能になり、段階的な移行が容易になります。

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+)/)
  ) {
    return NextResponse.redirect(
      new URL(`${req.nextUrl.pathname}/`, req.nextUrl)
    )
  }
}

skipMiddlewareUrlNormalizeは、Next.jsでのURL正規化を無効にすることで、直接訪問とクライアント遷移の処理を同じにすることができます。一部の高度なケースでは、このオプションにより元の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
}

ユニットテスト (実験的)

Next.js 15.1から、next/experimental/testing/serverパッケージには、ミドルウェアファイルのユニットテストを支援するユーティリティが含まれています。ミドルウェアのユニットテストは、コードが本番環境に到達する前に、目的のパスでのみ実行され、カスタムルーティングロジックが意図どおりに機能することを確認するのに役立ちます。

unstable_doesMiddlewareMatch関数は、提供されたURL、ヘッダー、およびクッキーに対してミドルウェアが実行されるかどうかをアサートするために使用できます。

import { unstable_doesMiddlewareMatch } from 'next/experimental/testing/server'
 
expect(
  unstable_doesMiddlewareMatch({
    config,
    nextConfig,
    url: '/test',
  })
).toEqual(false)

ミドルウェア関数全体もテストできます。

import { isRewrite, getRewrittenUrl } from 'next/experimental/testing/server'
 
const request = new NextRequest('https://nextjs.dokyumento.jp/docs')
const response = await middleware(request)
expect(isRewrite(response)).toEqual(true)
expect(getRewrittenUrl(response)).toEqual('https://other-domain.com/docs')
// getRedirectUrl could also be used if the response were a redirect

ランタイム

ミドルウェアはデフォルトでEdgeランタイムを使用します。v15.2 (canary) 以降では、Node.jsランタイムを使用するための実験的なサポートがあります。有効にするには、next.configファイルにフラグを追加してください。

next.config.ts
import type { NextConfig } from 'next'
 
const nextConfig: NextConfig = {
  experimental: {
    nodeMiddleware: true,
  },
}
 
export default nextConfig

次に、ミドルウェアファイルで、configオブジェクトのランタイムをnodejsに設定します。

middleware.ts
export const config = {
  runtime: 'nodejs',
}

注意: この機能はまだ本番環境での使用は推奨されていません。そのため、安定版ではなくnext@canaryリリースを使用している場合を除き、Next.jsはエラーをスローします。

バージョン履歴

バージョン変更点
v15.2.0ミドルウェアでNode.jsランタイムを使用できるようになりました (実験的)
v13.1.0高度なミドルウェアフラグが追加されました
v13.0.0ミドルウェアがリクエストヘッダー、レスポンスヘッダーを変更し、レスポンスを送信できるようになりました
v12.2.0ミドルウェアは安定版になりました。アップグレードガイドを参照してください。
v12.0.9Edge Runtimeで絶対URLを強制 (PR)
v12.0.0ミドルウェア (ベータ版) が追加されました