ミドルウェア
ミドルウェアは、リクエストが完了する前にコードを実行することを可能にします。これにより、受信リクエストに基づいて、書き換え、リダイレクト、リクエストまたはレスポンスヘッダーの変更、または直接応答によってレスポンスを変更できます。
ミドルウェアは、キャッシュされたコンテンツとルートがマッチングされる前に実行されます。詳細については、「パスのマッチング」を参照してください。
ユースケース
ミドルウェアが効果的な一般的なシナリオには、以下のようなものがあります。
- 受信リクエストの一部を読み取った後の迅速なリダイレクト
- A/Bテストや実験に基づいた異なるページへの書き換え
- すべてのページまたは一部のページのヘッダー変更
ミドルウェアは**適していません**
- 遅いデータフェッチ
- セッション管理
規約
ミドルウェアを定義するには、プロジェクトのルートに middleware.ts
(または .js
) ファイルを使用します。例えば、pages
や app
と同じレベル、または必要に応じて src
の中に配置します。
注意: プロジェクトごとにサポートされる
middleware.ts
ファイルは1つだけですが、ミドルウェアロジックをモジュール式に整理することは可能です。ミドルウェアの機能を個別の.ts
または.js
ファイルに分割し、それらをメインの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*',
}
パスのマッチング
ミドルウェアは、プロジェクト内のすべてのルーティングに対して呼び出されます。このため、特定のルートを正確にターゲットにしたり、除外したりするためにマッチャーを使用することが重要です。以下は実行順序です。
headers
fromnext.config.js
redirects
fromnext.config.js
- ミドルウェア (
rewrites
,redirects
など) beforeFiles
(rewrites
) fromnext.config.js
- ファイルシステムルート (
public/
,_next/static/
,pages/
,app/
など) afterFiles
(rewrites
) fromnext.config.js
- 動的ルーティング (
/blog/[slug]
) fallback
(rewrites
) fromnext.config.js
ミドルウェアが実行されるパスを定義する方法は2つあります。
マッチャー
matcher
を使用すると、特定のパスで実行するミドルウェアをフィルタリングできます。
export const config = {
matcher: '/about/:path*',
}
単一のパスまたは複数のパスを配列構文でマッチングできます。
export const config = {
matcher: ['/about/:path*', '/dashboard/:path*'],
}
The matcher
の設定は完全な正規表現をサポートしているため、否定先読みや文字マッチングのようなマッチングが可能です。特定のパスを除くすべてにマッチする否定先読みの例は、こちらで確認できます。
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
配列、あるいはその両方を組み合わせて使用することで、特定の要求に対してミドルウェアをバイパスすることもできます。
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
の値はビルド時に静的に解析できるよう定数である必要があります。変数などの動的な値は無視されます。
設定されたマッチャー
/
で始まる必要があります。- 名前付きパラメーターを含めることができます:
/about/:path
は/about/a
と/about/b
にマッチしますが、/about/a/c
にはマッチしません。 - 名前付きパラメーター (
:
で始まるもの) に修飾子を付けることができます:/about/:path*
は/about/a/b/c
にマッチします。これは*
が「ゼロ個以上」を意味するためです。?
は「ゼロ個または1個」、+
は「1個以上」を意味します。 - 括弧で囲まれた正規表現を使用できます:
/about/(.*)
は/about/:path*
と同じです。
詳細については、path-to-regexpのドキュメントを参照してください。
知っておくと便利: 下位互換性のため、Next.js は常に
/public
を/public/index
と見なします。したがって、/public/:path
のマッチャーはマッチします。
条件文
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
The NextResponse
APIは、以下を可能にします。
- 受信リクエストを別のURLに
redirect
する - 与えられたURLを表示してレスポンスを
rewrite
する - APIルーティング、
getServerSideProps
、およびrewrite
の宛先のリクエストヘッダーを設定する - レスポンスクッキーを設定する
- レスポンスヘッダーを設定する
ミドルウェアからレスポンスを生成するには、以下の方法があります。
- レスポンスを生成するルーティング (ページまたはEdge APIルーティング) に
rewrite
する NextResponse
を直接返す。詳細は「レスポンスの生成」を参照してください。
クッキーの使用
クッキーは通常のヘッダーです。Request
では Cookie
ヘッダーに、Response
では Set-Cookie
ヘッダーに格納されます。Next.js は、NextRequest
と NextResponse
の cookies
拡張を通じて、これらのクッキーにアクセスし、操作するための便利な方法を提供します。
- 受信リクエストの場合、
cookies
にはget
、getAll
、set
、およびdelete
の各メソッドが用意されています。has
でクッキーの存在を確認したり、clear
ですべてのクッキーを削除したりできます。 - 送信レスポンスの場合、
cookies
にはget
、getAll
、set
、およびdelete
の各メソッドが用意されています。
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 以降で利用可能です)。
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 ヘッダーを設定することで、単純なリクエストとプリフライトリクエストを含むクロスオリジンリクエストを許可できます。
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*',
}
レスポンスの生成
ミドルウェアから直接レスポンスを返すには、Response
または NextResponse
のインスタンスを返します。(これは Next.js v13.1.0 以降で利用可能です。)
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 }
)
}
}
waitUntil
とNextFetchEvent
NextFetchEvent
オブジェクトは、ネイティブの FetchEvent
オブジェクトを拡張したもので、waitUntil()
メソッドを含んでいます。
waitUntil()
メソッドは、引数としてプロミスを受け取り、そのプロミスが解決するまでミドルウェアのライフタイムを延長します。これはバックグラウンドで作業を実行する際に役立ちます。
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
では、高度なユースケースを処理するために、skipMiddlewareUrlNormalize
と skipTrailingSlashRedirect
という2つの追加フラグがミドルウェアに導入されました。
skipTrailingSlashRedirect
は、Next.js の末尾スラッシュの追加または削除に関するリダイレクトを無効にします。これにより、ミドルウェア内でカスタム処理を行い、一部のパスでは末尾スラッシュを維持し、他のパスでは維持しないことが可能になり、段階的な移行が容易になります。
module.exports = {
skipTrailingSlashRedirect: true,
}
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 を使用して完全な制御が可能になります。
module.exports = {
skipMiddlewareUrlNormalize: true,
}
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
ファイルにフラグを追加してください。
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
nodeMiddleware: true,
},
}
export default nextConfig
次に、ミドルウェアファイルで、config
オブジェクトのランタイムを nodejs
に設定します。
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.9 | Edge Runtimeで絶対URLを強制 (PR) |
v12.0.0 | ミドルウェア (ベータ版) が追加されました |
お役に立ちましたか?