メインコンテンツまでスキップ

Middleware

Middlewareを使用すると、リクエストが完了する前にコードを実行できます。受信したリクエストに基づいて、レスポンスをリライト、リダイレクト、リクエストまたはレスポンスヘッダーの変更、または直接応答することで修正できます。

Middlewareはキャッシュされたコンテンツやルートが一致する前に実行されます。詳細はMatching Pathsを参照してください。

ユースケース

Middlewareが効果的な一般的なシナリオには以下が含まれます:

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

Middlewareが適していないのは以下の場合です:

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

規約

Middlewareを定義するには、プロジェクトのrootにmiddleware.ts(または.js)ファイルを使用します。例えば、pagesappと同じレベル、または該当する場合はsrc内に配置します。

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

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

// この関数は`await`を使用する場合、`async`としてマークできます
export function middleware(request: NextRequest) {
return NextResponse.redirect(new URL('/home', request.url))
}

// 詳細は以下の"Matching Paths"を参照してください
export const config = {
matcher: '/about/:path*',
}

パスのマッチング

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

  1. next.config.jsheaders
  2. next.config.jsredirects
  3. Middleware(rewritesredirectsなど)
  4. next.config.jsbeforeFilesrewrites
  5. ファイルシステムルート(public/_next/static/pages/app/など)
  6. next.config.jsafterFilesrewrites
  7. 動的ルート(/blog/[slug]
  8. next.config.jsfallbackrewrites

Middlewareが実行されるパスを定義する方法は2つあります:

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

マッチャー

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

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: [
/*
* 以下で始まるリクエストパスを除くすべてのリクエストパスをマッチング:
* - api(APIルート)
* - _next/static(静的ファイル)
* - _next/image(画像最適化ファイル)
* - favicon.ico、sitemap.xml、robots.txt(メタデータファイル)
*/
'/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
],
}

missingまたはhas配列、またはその両方を組み合わせて、特定のリクエストをMiddlewareからバイパスすることもできます:

middleware.js
export const config = {
matcher: [
/*
* 以下で始まるリクエストパスを除くすべてのリクエストパスをマッチング:
* - api(APIルート)
* - _next/static(静的ファイル)
* - _next/image(画像最適化ファイル)
* - favicon.ico、sitemap.xml、robots.txt(メタデータファイル)
*/
{
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: matcherの値は定数である必要があるため、ビルド時に静的に解析できます。変数のような動的な値は無視されます。

設定されたマッチャー:

  1. /で始まる必要があります
  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のマッチャーはマッチします。

条件文

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 Routes、getServerSideProps、およびrewriteの宛先に対してリクエストヘッダーを設定する
  • レスポンスのcookieを設定する
  • レスポンスヘッダーを設定する

Middlewareからレスポンスを生成するには、以下の方法があります:

  1. レスポンスを生成するルート(PageまたはRoute Handler)にrewriteする
  2. NextResponseを直接返す。詳細はProducing a Responseを参照してください

Cookieの使用

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

  1. 受信リクエストの場合、cookiesには次のメソッドがあります:getgetAllset、およびdelete cookies。hasを使用してcookieの存在を確認したり、clearですべてのcookieを削除したりできます。
  2. 送信レスポンスの場合、cookiesには次のメソッドがあります:getgetAllset、およびdelete
middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
// 受信リクエストに"Cookie:nextjs=fast"ヘッダーが存在すると仮定します
// `RequestCookies` APIを使用してリクエストからcookieを取得します
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

// `ResponseCookies` APIを使用してレスポンスにcookieを設定します
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: '/' }
// 送信レスポンスには`Set-Cookie:vercel=fast;path=/`ヘッダーが含まれます。

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) {
// リクエストヘッダーをクローンし、新しいヘッダー`x-hello-from-middleware1`を設定します
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-hello-from-middleware1', 'hello')

// NextResponse.nextでリクエストヘッダーを設定することもできます
const response = NextResponse.next({
request: {
// 新しいリクエストヘッダー
headers: requestHeaders,
},
})

// 新しいレスポンスヘッダー`x-hello-from-middleware2`を設定します
response.headers.set('x-hello-from-middleware2', 'hello')
return response
}

Good to know: 大きなヘッダーを設定することは避けてください。バックエンドのWebサーバーの設定によっては431 Request Header Fields Too Largeエラーが発生する可能性があります。

CORS

Middlewareで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) {
// リクエストからオリジンを確認します
const origin = request.headers.get('origin') ?? ''
const isAllowedOrigin = allowedOrigins.includes(origin)

// プリフライトリクエストを処理します
const isPreflight = request.method === 'OPTIONS'

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

// シンプルリクエストを処理します
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: Route Handlersで個別のルートに対してCORSヘッダーを設定できます。

レスポンスの生成

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

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

// Middlewareを`/api/`で始まるパスに制限します
export const config = {
matcher: '/api/:function*',
}

export function middleware(request: NextRequest) {
// 認証関数を呼び出してリクエストを確認します
if (!isAuthenticated(request)) {
// エラーメッセージを示すJSONで応答します
return Response.json(
{ success: false, message: 'authentication failed' },
{ status: 401 }
)
}
}

waitUntilNextFetchEvent

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

waitUntil()メソッドは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()
}

高度なMiddlewareフラグ

Next.jsのv13.1では、skipMiddlewareUrlNormalizeskipTrailingSlashRedirectという2つの追加フラグがミドルウェアに導入され、高度なユースケースに対応しています。

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

// トレーリングスラッシュの処理を適用します
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)
// フラグを使用すると、これは/_next/data/build-id/hello.jsonになります
// フラグを使用しない場合、これは/helloに正規化されます
}

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

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

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

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.org/docs')
const response = await middleware(request)
expect(isRewrite(response)).toEqual(true)
expect(getRewrittenUrl(response)).toEqual('https://other-domain.com/docs')
// レスポンスがリダイレクトであれば、getRedirectUrlも使用できます

ランタイム

MiddlewareはデフォルトでEdge runtimeを使用します。v15.2(canary)以降、Node.js runtimeを使用するための実験的なサポートがあります。有効にするには、next.configファイルにフラグを追加します:

next.config.ts
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
experimental: {
nodeMiddleware: true,
},
}

export default nextConfig

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

export const config = {
runtime: 'nodejs',
}
export const config = {
runtime: 'nodejs',
}

Note: この機能はまだ本番環境での使用は推奨されていません。そのため、Next.jsは安定版リリースの代わりにnext@canaryリリースを使用していない限りエラーをスローします。

バージョン履歴

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