プロキシ
注意:
middlewareファイル規約は非推奨となり、proxyに名称が変更されました。詳細は Proxyへの移行 を参照してください。
proxy.js|ts ファイルは、Proxy を記述し、リクエストが完了する前にサーバーでコードを実行するために使用されます。その後、受信したリクエストに基づいて、リダイレクト、リクエストまたはレスポンスヘッダーの変更、または直接レスポンスを返すことで、レスポンスを修正できます。
Proxy はルートのレンダリングより前に実行されます。認証、ロギング、リダイレクトの処理など、カスタムサーバーサイドロジックの実装に特に役立ちます。
知っておくと良いこと:
Proxy はレンダリングコードとは別に呼び出されることを想定しており、最適化されたケースでは高速なリダイレクト/書き換え処理のために CDN にデプロイされるため、共有モジュールやグローバル変数に依存しないでください。
Proxy からアプリケーションに情報を渡すには、ヘッダー、Cookie、書き換え、リダイレクト、または URL を使用します。
プロジェクトのルート、または該当する場合はsrc内にproxy.ts(または.js)ファイルを作成します。これにより、pagesまたはappと同じレベルに配置されます。
pageExtensions をカスタマイズした場合(例: .page.ts または .page.js)、ファイル名をそれぞれ proxy.page.ts または proxy.page.js にしてください。
import { NextResponse, NextRequest } from 'next/server'
// This function can be marked `async` if using `await` inside
export function proxy(request: NextRequest) {
return NextResponse.redirect(new URL('/home', request.url))
}
export const config = {
matcher: '/about/:path*',
}エクスポート
Proxy関数
ファイルは、デフォルトエクスポートまたは名前付きエクスポートproxyとして、単一の関数をエクスポートする必要があります。同じファイルから複数のプロキシをエクスポートすることはサポートされていません。
// Example of default export
export default function proxy(request) {
// Proxy logic
}設定オブジェクト(オプション)
オプションで、Proxy関数と並行して設定オブジェクトをエクスポートできます。このオブジェクトには、Proxyが適用されるパスを指定するためのmatcherが含まれています。
Matcher
matcherオプションを使用すると、Proxyを実行する特定のパスをターゲットにできます。これらのパスはいくつかの方法で指定できます。
- 単一パスの場合: 文字列を直接使用してパスを指定します。例:
'/about'。 - 複数パスの場合: 配列を使用して複数のパスをリストします。例:
matcher: ['/about', '/contact']。これにより、Proxyは/aboutと/contactの両方に適用されます。
export const config = {
matcher: ['/about/:path*', '/dashboard/:path*'],
}さらに、matcherオプションは正規表現による複雑なパス指定もサポートしています。例: matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)']。これにより、含めるパスまたは除外するパスを正確に制御できます。
matcherオプションは、以下のキーを持つオブジェクトの配列を受け入れます。
source: リクエストパスを一致させるために使用されるパスまたはパターン。直接のパス一致の場合は文字列、より複雑な一致の場合はパターンになります。regexp(オプション): sourceに基づいて一致を微調整する正規表現文字列。含めるパスまたは除外するパスをさらに制御できます。locale(オプション): `false`に設定すると、パス一致におけるロケールベースのルーティングを無視します。has(オプション): ヘッダー、クエリパラメータ、Cookieなどの特定の要求要素の存在に基づく条件を指定します。missing(オプション): ヘッダーやCookieが欠落しているなど、特定の要求要素が存在しない条件に焦点を当てます。
export const config = {
matcher: [
{
source: '/api/*',
regexp: '^/api/(.*)',
locale: false,
has: [
{ type: 'header', key: 'Authorization', value: 'Bearer Token' },
{ type: 'query', key: 'userId', value: '123' },
],
missing: [{ type: 'cookie', key: 'session', value: 'active' }],
},
],
}設定されたmatcher
/で始まる必要があります。- 名前付きパラメータを含めることができます:
/about/:pathは/about/aと/about/bに一致しますが、/about/a/cには一致しません。 - 名前付きパラメータに修飾子(
:で始まる)を付けることができます:/about/:path*は/about/a/b/cに一致します。これは*がゼロ個以上を意味するためです。?はゼロ個または1個、+は1個以上を意味します。 - 括弧で囲まれた正規表現を使用できます:
/about/(.*)は/about/:path*と同じです。
path-to-regexp ドキュメントで詳細を参照してください。
知っておくと良いこと:
matcherの値は定数である必要があります。これにより、ビルド時に静的に解析できます。変数のような動的な値は無視されます。- 後方互換性のために、Next.js は常に
/publicを/public/indexとして扱います。したがって、/public/:pathという matcher は一致します。
Params
request
Proxy を定義する際、デフォルトエクスポート関数は単一のパラメータ request を受け取ります。このパラメータは NextRequest のインスタンスであり、受信した HTTP リクエストを表します。
import type { NextRequest } from 'next/server'
export function proxy(request: NextRequest) {
// Proxy logic goes here
}知っておくと良いこと:
NextRequestは Next.js Proxy における受信 HTTP リクエストを表す型であり、NextResponseは HTTP レスポンスを操作して返すために使用されるクラスです。
NextResponse
NextResponse API を使用すると、次のことが可能です。
- 受信したリクエストを別の URL に
redirectする - 指定した URL を表示することで、レスポンスを
rewriteする - API Routes、
getServerSideProps、およびrewriteの宛先のためにリクエストヘッダーを設定する - レスポンス Cookie を設定する
- レスポンスヘッダーを設定する
Proxy からレスポンスを生成するには、次のことができます。
- レスポンスを生成するルート(Page または Edge API Route)に
rewriteする。 - 直接
NextResponseを返します。レスポンスの生成を参照してください。
実行順序
Proxy はプロジェクト内のすべてのルートに対して呼び出されます。このため、特定のルートを正確にターゲットまたは除外するために matcher を使用することが重要です。実行順序は次のとおりです。
next.config.jsからのheadersnext.config.jsからのredirects- Proxy (
rewrites,redirectsなど) next.config.jsからのbeforeFiles(rewrites)- ファイルシステムルート (
public/,_next/static/,pages/,app/など) next.config.jsからのafterFiles(rewrites)- 動的ルート (
/blog/[slug]) next.config.jsからのfallback(rewrites)
Runtime
Proxy はデフォルトで Node.js ランタイムを使用します。runtime 設定オプションは Proxy ファイルでは利用できません。Proxy で runtime 設定オプションを設定するとエラーが発生します。
高度なProxyフラグ
Next.js v13.1 で、高度なユースケースを処理するための 2 つの追加フラグ skipMiddlewareUrlNormalize と skipTrailingSlashRedirect が Proxy に導入されました。
skipTrailingSlashRedirect は、末尾のスラッシュの追加または削除に対する Next.js のリダイレクトを無効にします。これにより、Proxy 内でカスタム処理を行って、一部のパスでは末尾のスラッシュを維持し、他のパスでは維持しないことが可能になり、増分移行が容易になります。
module.exports = {
skipTrailingSlashRedirect: true,
}const legacyPrefixes = ['/docs', '/blog']
export default async function proxy(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 は、URL の正規化を無効にすることで、直接アクセスとクライアント遷移を同じように処理できるようにします。一部の高度なケースでは、このオプションにより、元の URL を使用して完全な制御が可能になります。
module.exports = {
skipMiddlewareUrlNormalize: true,
}export default async function proxy(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
}例
条件分岐
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function proxy(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))
}
}Cookieの使用
Cookie は通常のヘッダーです。Request では Cookie ヘッダーに格納されます。Response では Set-Cookie ヘッダーに格納されます。Next.js は NextRequest および NextResponse の cookies 拡張機能を通じて、これらの Cookie にアクセスおよび操作するための便利な方法を提供します。
- 受信リクエストの場合、
cookiesはget、getAll、set、deleteのメソッドを備えています。hasで Cookie の存在を確認したり、clearですべての Cookie を削除したりできます。 - 送信レスポンスの場合、
cookiesにはget、getAll、set、deleteのメソッドがあります。
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function proxy(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 proxy(request: NextRequest) {
// Clone the request headers and set a new header `x-hello-from-proxy1`
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-hello-from-proxy1', '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-proxy2`
response.headers.set('x-hello-from-proxy2', 'hello')
return response
}スニペットが使用している点に注意してください。
requestHeadersを上流で利用可能にするためにNextResponse.next({ request: { headers: requestHeaders } })- ではなく、
requestHeadersをクライアントで利用可能にするNextResponse.next({ headers: requestHeaders })
Proxy の NextResponse ヘッダーについては、NextResponse headers in Proxy で詳細を確認してください。
注意: 大量のヘッダーを設定すると、バックエンド Web サーバーの設定によっては 431 Request Header Fields Too Large エラーが発生する可能性があるため、避けてください。
CORS
Proxy で 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 proxy(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*',
}レスポンスの生成
Proxy から直接レスポンスを生成するには、Response または NextResponse インスタンスを返します。(これは Next.js v13.1.0 以降で利用可能です)
import type { NextRequest } from 'next/server'
import { isAuthenticated } from '@lib/auth'
// Limit the proxy to paths starting with `/api/`
export const config = {
matcher: '/api/:function*',
}
export function proxy(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 }
)
}
}否定マッチング
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 配列、またはその両方を組み合わせて使用することで、特定の要求に対して Proxy をバイパスすることもできます。
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' }],
},
],
}waitUntil と NextFetchEvent
NextFetchEvent オブジェクトは、ネイティブな FetchEvent オブジェクトを拡張し、waitUntil() メソッドが含まれています。
waitUntil() メソッドはプロミスを引数として受け取り、プロミスが解決されるまで Proxy のライフタイムを延長します。これはバックグラウンドでの処理に役立ちます。
import { NextResponse } from 'next/server'
import type { NextFetchEvent, NextRequest } from 'next/server'
export function proxy(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 15.1 以降、next/experimental/testing/server パッケージには、Proxy ファイルの単体テストを支援するユーティリティが含まれています。Proxy の単体テストは、コードが本番環境に到達する前に、Proxy が意図したパスでのみ実行され、カスタムルーティングロジックが期待どおりに機能することを確認するのに役立ちます。
unstable_doesProxyMatch 関数を使用して、Proxy が提供された URL、ヘッダー、Cookie に対して実行されるかどうかをアサートできます。
import { unstable_doesProxyMatch } from 'next/experimental/testing/server'
expect(
unstable_doesProxyMatch({
config,
nextConfig,
url: '/test',
})
).toEqual(false)Proxy 関数全体をテストすることもできます。
import { isRewrite, getRewrittenUrl } from 'next/experimental/testing/server'
const request = new NextRequest('https://nextjs.dokyumento.jp/docs')
const response = await proxy(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プラットフォームサポート
| デプロイメントオプション | サポート |
|---|---|
| Node.jsサーバー | はい |
| Dockerコンテナ | はい |
| 静的エクスポート | いいえ |
| アダプター | プラットフォーム固有 |
Next.js のセルフホスティング時に Proxy を設定する方法を学ぶ。
Proxyへの移行
変更の理由
middleware という名称が、Express.js のミドルウェアと混同されやすく、その目的の誤解を招く可能性があるため、名称変更に至りました。また、Middleware は非常に多機能ですが、これは最後の手段として使用することが推奨されます。
Next.js は、開発者が Middleware を使用せずに目標を達成できるよう、より人間工学的で優れた API を提供することを目指しています。これが middleware の名称変更の理由です。
"Proxy" という名前の理由
Proxy という名前は、Middleware が何ができるのかを明確にします。「proxy」という言葉は、アプリの前面にネットワーク境界があることを示唆しており、これが Middleware の動作です。また、Middleware はデフォルトで Edge Runtime で実行され、クライアントに近い場所で、アプリのリージョンから分離して実行できます。これらの動作は「proxy」という用語により合致し、機能の目的をより明確にしています。
移行方法
Middleware に他の選択肢がない場合を除き、Middleware への依存を避けることをお勧めします。目標は、開発者が Middleware なしで目標を達成できるよう、より人間工学的な API を提供することです。
「middleware」という用語は、ユーザーを Express.js middleware と混同させやすく、誤用を助長する可能性があります。方向性を明確にするために、ファイル規約を「proxy」に変更します。これは、Middleware から離れ、その過負荷な機能を分解し、Proxy の目的を明確にすることを示しています。
Next.js は、middleware.ts から proxy.ts へ移行するための codemod を提供します。移行するには、次のコマンドを実行します。
npx @next/codemod@canary middleware-to-proxy .codemod は、ファイル名と関数名を middleware から proxy に変更します。
// middleware.ts -> proxy.ts
- export function middleware() {
+ export function proxy() {バージョン履歴
| バージョン | 変更履歴 |
|---|---|
v16.0.0 | Middleware は非推奨となり、Proxy に名称変更されました。 |
v15.5.0 | Middleware は Node.js ランタイムを使用できるようになりました(安定版)。 |
v15.2.0 | Middleware は Node.js ランタイムを使用できるようになりました(実験的)。 |
v13.1.0 | 高度な Middleware フラグが追加されました。 |
v13.0.0 | Middleware はリクエストヘッダー、レスポンスヘッダーを変更し、レスポンスを送信できます。 |
v12.2.0 | Middleware は安定版です。アップグレードガイドを参照してください。 |
v12.0.9 | Edge Runtime で絶対 URL を強制する(PR) |
v12.0.0 | Middleware (ベータ版) を追加しました。 |
役に立ちましたか?