Menu

リライト

リライトを使用すると、着信リクエストパスを別の宛先パスにマッピングできます。

リライトはURLプロキシとして機能し、宛先パスを隠蔽し、ユーザーがサイト上の位置を変更していないように見せます。対照的に、リダイレクトは新しいページに再ルーティングし、URLの変更を表示します。

リライトを使用するには、next.config.jsrewritesキーを使用します:

next.config.js
module.exports = {
  async rewrites() {
    return [
      {
        source: '/about',
        destination: '/',
      },
    ]
  },
}

リライトはクライアントサイドルーティングに適用され、上記の例では<Link href="/about">にリライトが適用されます。

rewritesは、sourcedestinationプロパティを持つオブジェクトを含む配列またはオブジェクトの配列を返す非同期関数です:

  • source: String - 着信リクエストパスパターン。
  • destination: String - ルーティングする先のパス。
  • basePath: falseまたはundefined - falseの場合、マッチング時にbasePathが含まれません。外部リライトにのみ使用できます。
  • locale: falseまたはundefined - ロケールをマッチング時に含めないかどうか。
  • hasは、typekeyvalueプロパティを持つhasオブジェクトの配列です。
  • missingは、typekeyvalueプロパティを持つmissingオブジェクトの配列です。

rewrites関数が配列を返すと、リライトはファイルシステム(ページと/publicファイル)をチェックした後、動的ルートの前に適用されます。rewrites関数が特定の形状のオブジェクトの配列を返す場合、Next.js v10.1以降、この動作をより細かく制御できます:

next.config.js
module.exports = {
  async rewrites() {
    return {
      beforeFiles: [
        // これらのリライトは、ヘッダー/リダイレクトの後、
        // _next/publicファイルを含むすべてのファイルの前にチェックされます。
        // ページファイルを上書きできます
        {
          source: '/some-page',
          destination: '/somewhere-else',
          has: [{ type: 'query', key: 'overrideMe' }],
        },
      ],
      afterFiles: [
        // これらのリライトは、ページ/publicファイルを
        // チェックした後、動的ルートの前にチェックされます
        {
          source: '/non-existent',
          destination: '/somewhere-else',
        },
      ],
      fallback: [
        // これらのリライトは、ページ/publicファイルと
        // 動的ルートの両方をチェックした後にチェックされます
        {
          source: '/:path*',
          destination: `https://my-old-site.com/:path*`,
        },
      ],
    }
  },
}

補足: beforeFilesのリライトは、ソースにマッチした直後にファイルシステム/動的ルートをすぐにチェックするわけではなく、すべてのbeforeFilesがチェックされるまで続行されます。

Next.jsのルートがチェックされる順序は:

  1. ヘッダーがチェック/適用される
  2. リダイレクトがチェック/適用される
  3. beforeFilesのリライトがチェック/適用される
  4. publicディレクトリからの静的ファイル、_next/staticファイル、非動的ページがチェック/提供される
  5. afterFilesのリライトがチェック/適用される。これらのリライトのいずれかがマッチした場合、各マッチ後に動的ルート/静的ファイルをチェックする
  6. fallbackのリライトがチェック/適用される。これらは404ページをレンダリングする前、動的ルート/すべての静的アセットをチェックした後に適用される。getStaticPathsfallback: true/'blocking'を使用する場合、next.config.jsで定義されたfallbackのrewritesは実行されない。

リライトパラメータ

リライト時にパラメータを使用する場合、パラメータがdestinationで使用されていない場合、デフォルトでクエリに渡されます。

next.config.js
module.exports = {
  async rewrites() {
    return [
      {
        source: '/old-about/:path*',
        destination: '/about', // :pathパラメータはここでは使用されないため、自動的にクエリに渡されます
      },
    ]
  },
}

パラメータが宛先で使用される場合、パラメータは自動的にクエリに渡されません。

next.config.js
module.exports = {
  async rewrites() {
    return [
      {
        source: '/docs/:path*',
        destination: '/:path*', // :pathパラメータはここで使用されるため、自動的にクエリに渡されません
      },
    ]
  },
}

宛先でパラメータが既に使用されている場合でも、クエリでパラメータを手動で渡すことができます。

next.config.js
module.exports = {
  async rewrites() {
    return [
      {
        source: '/:first/:second',
        destination: '/:first?second=:second',
        // :firstパラメータが宛先で使用されているため、
        // :secondパラメータは自動的にクエリに追加されませんが、
        // 上記のように手動で追加できます
      },
    ]
  },
}

補足: 自動静的最適化またはプリレンダリングからの静的ページは、ハイドレーション後にクライアント上でリライトのパラメータを解析し、クエリに提供されます。

パスマッチング

パスマッチが許可されています。例えば /blog/:slug/blog/hello-world にマッチします(ネストされたパスは対象外):

next.config.js
module.exports = {
  async rewrites() {
    return [
      {
        source: '/blog/:slug',
        destination: '/news/:slug', // マッチしたパラメータは宛先で使用できます
      },
    ]
  },
}

ワイルドカードパスマッチング

ワイルドカードパスをマッチさせるには、パラメータの後に * を使用します。例えば /blog/:slug*/blog/a/b/c/d/hello-world にマッチします:

next.config.js
module.exports = {
  async rewrites() {
    return [
      {
        source: '/blog/:slug*',
        destination: '/news/:slug*', // マッチしたパラメータは宛先で使用できます
      },
    ]
  },
}

正規表現パスマッチング

正規表現パスをマッチさせるには、パラメータの後に括弧で正規表現を囲みます。例えば /blog/:slug(\\d{1,})/blog/123 にマッチしますが、/blog/abc にはマッチしません:

next.config.js
module.exports = {
  async rewrites() {
    return [
      {
        source: '/old-blog/:post(\\d{1,})',
        destination: '/blog/:post', // マッチしたパラメータは宛先で使用できます
      },
    ]
  },
}

以下の文字 (, ), {, }, [, ], |, \, ^, ., :, *, +, -, ?, $ は正規表現のパスマッチングに使用されるため、source で特殊でない値として使用する場合は、その前に \\ を追加してエスケープする必要があります:

next.config.js
module.exports = {
  async rewrites() {
    return [
      {
        // これは `/english(default)/something` のリクエストにマッチします
        source: '/english\\(default\\)/:slug',
        destination: '/en-us/:slug',
      },
    ]
  },
}

ヘッダー、Cookie、クエリのマッチング

リライトを適用するために、ヘッダー、Cookie、クエリの値が has フィールドにマッチするか、missing フィールドにマッチしない場合のみマッチさせることができます。リライトを適用するには、source とすべての has アイテム、すべての missing アイテムがマッチする必要があります。

hasmissing アイテムには、以下のフィールドを設定できます:

  • type: String - headercookiehostquery のいずれかでなければなりません。
  • key: String - マッチさせる選択したタイプのキー。
  • value: String または undefined - チェックする値。未定義の場合、任意の値にマッチします。正規表現のような文字列を使用して値の特定の部分をキャプチャできます。例えば、値 first-(?<paramName>.*)first-second に使用すると、:paramName を使用して宛先で second を使用できます。
next.config.js
module.exports = {
  async rewrites() {
    return [
      // ヘッダー `x-rewrite-me` が存在する場合、
      // このリライトが適用されます
      {
        source: '/:path*',
        has: [
          {
            type: 'header',
            key: 'x-rewrite-me',
          },
        ],
        destination: '/another-page',
      },
      // ヘッダー `x-rewrite-me` が存在しない場合、
      // このリライトが適用されます
      {
        source: '/:path*',
        missing: [
          {
            type: 'header',
            key: 'x-rewrite-me',
          },
        ],
        destination: '/another-page',
      },
      // ソース、クエリ、Cookieがマッチする場合、
      // このリライトが適用されます
      {
        source: '/specific/:path*',
        has: [
          {
            type: 'query',
            key: 'page',
            // 値が提供されており、名前付きキャプチャグループ(例:(?<page>home))を使用していないため、
            // ページ値は宛先で使用できません
            value: 'home',
          },
          {
            type: 'cookie',
            key: 'authorized',
            value: 'true',
          },
        ],
        destination: '/:path*/home',
      },
      // ヘッダー `x-authorized` が存在し、
      // マッチする値を含む場合、このリライトが適用されます
      {
        source: '/:path*',
        has: [
          {
            type: 'header',
            key: 'x-authorized',
            value: '(?<authorized>yes|true)',
          },
        ],
        destination: '/home?authorized=:authorized',
      },
      // ホストが `example.com` の場合、
      // このリライトが適用されます
      {
        source: '/:path*',
        has: [
          {
            type: 'host',
            value: 'example.com',
          },
        ],
        destination: '/another-page',
      },
    ]
  },
}

外部URLへのリライト

リライトを使用して外部URLにリライトできます。これは、Next.jsを段階的に導入する際に特に便利です。以下は、メインアプリの /blog ルートを外部サイトにリダイレクトする例です。

next.config.js
module.exports = {
  async rewrites() {
    return [
      {
        source: '/blog',
        destination: 'https://example.com/blog',
      },
      {
        source: '/blog/:slug',
        destination: 'https://example.com/blog/:slug', // マッチしたパラメータを宛先で使用できます
      },
    ]
  },
}

trailingSlash: true を使用している場合、source パラメータにも末尾のスラッシュを挿入する必要があります。宛先サーバーも末尾のスラッシュを期待している場合は、destination パラメータにも含める必要があります。

next.config.js
module.exports = {
  trailingSlash: true,
  async rewrites() {
    return [
      {
        source: '/blog/',
        destination: 'https://example.com/blog/',
      },
      {
        source: '/blog/:path*/',
        destination: 'https://example.com/blog/:path*/',
      },
    ]
  },
}

Next.jsの段階的な導入

Next.jsがすべてのNext.jsルートをチェックした後、既存のウェブサイトにプロキシするように設定することもできます。

この方法により、より多くのページをNext.jsに移行する際にリライト設定を変更する必要がありません。

next.config.js
module.exports = {
  async rewrites() {
    return {
      fallback: [
        {
          source: '/:path*',
          destination: `https://custom-routes-proxying-endpoint.vercel.app/:path*`,
        },
      ],
    }
  },
}

basePath対応のリライト

basePath対応を利用する場合、各 sourcedestination には自動的に basePath が接頭辞として追加されます。ただし、リライトに basePath: false を追加した場合は除きます:

next.config.js
module.exports = {
  basePath: '/docs',
 
  async rewrites() {
    return [
      {
        source: '/with-basePath', // 自動的に /docs/with-basePath になります
        destination: '/another', // 自動的に /docs/another になります
      },
      {
        // basePath: false が設定されているため、/docs は /without-basePath に追加されません
        // 注意:内部リライト(例:`destination: '/another'`)には使用できません
        source: '/without-basePath',
        destination: 'https://example.com',
        basePath: false,
      },
    ]
  },
}

i18nサポート付きリライト

i18n対応を利用する場合、各 sourcedestination には、リライトに locale: false を追加しない限り、設定された locales を処理するために自動的に接頭辞が追加されます。locale: false を使用する場合、正しくマッチさせるためにソースと宛先にロケールを接頭辞として付ける必要があります。

next.config.js
module.exports = {
  i18n: {
    locales: ['en', 'fr', 'de'],
    defaultLocale: 'en',
  },
 
  async rewrites() {
    return [
      {
        source: '/with-locale', // 自動的にすべてのロケールを処理します
        destination: '/another', // 自動的にロケールを引き継ぎます
      },
      {
        // locale: false が設定されているため、ロケールを自動的に処理しません
        source: '/nl/with-locale-manual',
        destination: '/nl/another',
        locale: false,
      },
      {
        // `en` がデフォルトロケールであるため、'/' にマッチします
        source: '/en',
        destination: '/en/another',
        locale: false,
      },
      {
        // locale: false が設定されている場合でも、すべてのロケールをマッチさせることができます
        source: '/:locale/api-alias/:path*',
        destination: '/api/:path*',
        locale: false,
      },
      {
        // これは /(en|fr|de)/(.*) に変換されるため、/:path* のようなトップレベルの
        // `/` または `/fr` ルートにはマッチしません
        source: '/(.*)',
        destination: '/another',
      },
    ]
  },
}

バージョン履歴

バージョン変更点
v13.3.0missing が追加されました。
v10.2.0has が追加されました。
v9.5.0ヘッダーが追加されました。