コンテンツにスキップ

proxy.js

注意:middleware ファイル規約は非推奨となり、proxy に名前が変更されました。詳細については、Proxyへの移行を参照してください。

proxy.js|ts ファイルは、Proxy を記述し、リクエストが完了する前にサーバーでコードを実行するために使用されます。その後、受信したリクエストに基づいて、レスポンスを書き換え、リダイレクトし、リクエストまたはレスポンスヘッダーを変更し、または直接レスポンスを返すことで、レスポンスを修正できます。

Proxy はルートがレンダリングされる前に実行されます。認証、ログ記録、リダイレクトの処理など、カスタムサーバーサイドロジックの実装に特に役立ちます。

知っておくと良いこと:

Proxy は、レンダリングコードとは別に呼び出されることを想定しており、最適化されたケースではCDNにデプロイして高速なリダイレクト/書き換え処理を行うべきです。共有モジュールやグローバル変数に依存しないでください。

Proxy からアプリケーションに情報を渡すには、ヘッダークッキー書き換えリダイレクト、またはURLを使用してください。

プロジェクトのルート、または該当する場合はsrc内にproxy.ts(または.js)ファイルを作成します。これにより、pagesまたはappと同じレベルに配置されます。

pageExtensions をカスタマイズした場合(例: .page.ts または .page.js)、ファイル名をそれぞれproxy.page.ts または proxy.page.js にしてください。

proxy.ts
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.js
// Example of default export
export default function proxy(request) {
  // Proxy logic
}

設定オブジェクト(オプション)

オプションで、プロキシ関数と並行して設定オブジェクトをエクスポートできます。このオブジェクトには、プロキシを適用するパスを指定するためのマッチャーが含まれています。

マッチャー

matcherオプションを使用すると、プロキシを実行する特定のパスをターゲットにできます。これらのパスはいくつかの方法で指定できます。

  • 単一パスの場合: 文字列を直接使用してパスを指定します。例: '/about'
  • 複数パスの場合: 配列を使用して複数のパスをリストします。例: matcher: ['/about', '/contact']。これは、/about/contactの両方にプロキシを適用します。
proxy.js
export const config = {
  matcher: ['/about/:path*', '/dashboard/:path*'],
}

さらに、matcherオプションは正規表現による複雑なパス指定もサポートしています。例: matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)']。これにより、含めるパスまたは除外するパスを正確に制御できます。

matcherオプションは、以下のキーを持つオブジェクトの配列を受け入れます。

  • source: リクエストパスを照合するために使用されるパスまたはパターン。直接のパス照合のための文字列、またはより複雑な照合のためのパターンにすることができます。
  • regexp(オプション): sourceに基づいて照合を微調整する正規表現文字列。どのパスを含めるか除外するかについて、追加の制御を提供します。
  • locale(オプション): falseに設定すると、パス照合でロケールベースのルーティングを無視します。
  • has(オプション): ヘッダー、クエリパラメータ、またはクッキーなどの特定の要求要素の存在に基づく条件を指定します。
  • missing(オプション): ヘッダーやクッキーがないなど、特定の要求要素が存在しない条件に焦点を当てます。
proxy.js
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' }],
    },
  ],
}

設定されたマッチャー

  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 のドキュメントで詳細を確認してください。

知っておくと良いこと:

  • matcherの値は定数である必要があります。これにより、ビルド時に静的に解析できます。変数のような動的な値は無視されます。
  • 下位互換性のために、Next.jsは常に/public/public/indexとして扱います。したがって、/public/:pathのマッチャーは一致します。

パラメーター

request

プロキシを定義する際、デフォルトエクスポート関数は単一のパラメーターrequestを受け取ります。このパラメーターはNextRequestのインスタンスであり、受信したHTTPリクエストを表します。

proxy.ts
import type { NextRequest } from 'next/server'
 
export function proxy(request: NextRequest) {
  // Proxy logic goes here
}

知っておくと良いこと:

  • NextRequestは、Next.jsプロキシで受信HTTPリクエストを表す型であり、NextResponseはHTTPレスポンスを操作して返すために使用されるクラスです。

NextResponse

NextResponse APIを使用すると、以下のことが可能です。

  • 受信したリクエストを別のURLにredirectする
  • 指定されたURLを表示してレスポンスをrewriteする
  • API Routes、getServerSideProps、およびrewriteの宛先のリクエストヘッダーを設定する
  • レスポンスクッキーを設定する
  • レスポンスヘッダーを設定する

Proxy からレスポンスを生成するには、以下のいずれかを行います。

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

知っておくと良いこと: リダイレクトの場合、NextResponse.redirectの代わりにResponse.redirectを使用することもできます。

実行順序

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

  1. next.config.jsからのheaders
  2. next.config.jsからのredirects
  3. Proxy(rewritesredirectsなど)
  4. next.config.jsからのbeforeFilesrewrites
  5. ファイルシステムルート(public/_next/static/pages/app/など)
  6. next.config.jsからのafterFilesrewrites
  7. 動的ルート(/blog/[slug]
  8. next.config.jsからのfallbackrewrites

ランタイム

ProxyはデフォルトでNode.jsランタイムを使用します。runtime設定オプションはProxyファイルでは利用できません。Proxyでruntime設定オプションを設定するとエラーが発生します。

高度なプロキシフラグ

Next.js v13.1 で、プロキシ用のskipMiddlewareUrlNormalizeskipTrailingSlashRedirectという2つの追加フラグが導入され、高度なユースケースに対応できるようになりました。

skipTrailingSlashRedirectは、末尾のスラッシュの追加または削除に対するNext.jsのリダイレクトを無効にします。これにより、プロキシ内でカスタム処理を行って、一部のパスでは末尾のスラッシュを維持し、他のパスでは維持しないようにすることができ、段階的な移行を容易にします。

next.config.js
module.exports = {
  skipTrailingSlashRedirect: true,
}
proxy.js
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を使用して完全な制御が可能になります。

next.config.js
module.exports = {
  skipMiddlewareUrlNormalize: true,
}
proxy.js
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
}

条件分岐

proxy.ts
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))
  }
}

クッキーの使用

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

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

proxy.ts
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ヘッダーを設定して、単純なリクエストおよびプリフライトリクエストを含むクロスオリジンリクエストを許可できます。

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

知っておくと良いこと: 個々のルートのCORSヘッダーは、ルートハンドラーで設定できます。

レスポンスの生成

Proxy から直接レスポンスを返すには、ResponseまたはNextResponseインスタンスを返します。(これはNext.js v13.1.0以降で利用可能です)

proxy.ts
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設定は完全な正規表現をサポートしているため、ネガティブルックアヘッドや文字マッチングなどのマッチングがサポートされています。特定のパスを除くすべてに一致するネガティブルックアヘッドの例をここに示します。

proxy.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配列、あるいはその両方の組み合わせを使用することで、特定の要求に対してProxyをバイパスすることもできます。

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

waitUntil および NextFetchEvent

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

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

proxy.ts
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パッケージには、プロキシファイルを単体テストするためのユーティリティが含まれています。プロキシを単体テストすることで、コードが本番環境に到達する前に、期待どおりのパスでのみ実行されること、およびカスタムルーティングロジックが意図したとおりに機能することを確認できます。

unstable_doesProxyMatch関数を使用して、指定されたURL、ヘッダー、およびクッキーに対してプロキシが実行されるかどうかをアサートできます。

import { unstable_doesProxyMatch } from 'next/experimental/testing/server'
 
expect(
  unstable_doesProxyMatch({
    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 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の名前が変更された理由は、「middleware」という言葉がExpress.jsのmiddlewareと混同されやすく、その目的を誤解する可能性があるためです。また、Middlewareは非常に多機能ですが、これは最終手段として使用することが推奨されます。

Next.jsは、開発者がMiddlewareなしで目標を達成できるように、より優れたAPIとより良いエルゴノミクスを提供するために前進しています。これがmiddlewareの名前が変更された理由です。

「Proxy」という名前の理由

Proxy という名前は、Middleware が何ができるかを明確にします。「proxy」という言葉は、アプリの前にネットワーク境界があることを示唆しており、これは Middleware の動作です。また、Middleware はデフォルトで Edge Runtime で実行され、クライアントの近く、アプリのリージョンから分離して実行できます。これらの動作は「proxy」という言葉とよりよく一致し、機能の目的をより明確にします。

移行方法

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.0Middleware が非推奨となり、Proxy にリネームされました。
v15.5.0Middleware で Node.js ランタイムが使用できるようになりました(安定版)。
v15.2.0Middleware で Node.js ランタイムが使用できるようになりました(実験的)。
v13.1.0高度な Middleware フラグが追加されました。
v13.0.0Middleware がリクエストヘッダー、レスポンスヘッダーを変更し、レスポンスを送信できるようになりました。
v12.2.0Middleware は安定版です。アップグレードガイドを参照してください。
v12.0.9Edge Runtime で絶対 URL を強制(PR
v12.0.0Middleware(ベータ版)が追加されました。