🚀

【Next.js和訳】API Reference/next.config.js/Rewrites

10 min read

この記事について

株式会社 UnReact はプロジェクトの一環としてNext.js ドキュメントの和訳を行っています。

この記事は、next.config.js/Rewritesの記事を和訳したものです。

記事内で使用する画像は、公式ドキュメント内の画像を引用して使用させていただいております。

リライト

バージョン履歴

バージョンの変更点
v10.2.0 has を追加しました。
v9.5.0 Rewrites を追加しました。

リライトは、入力されたリクエストのパスを異なる宛先パスにマッピングすることができます。

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

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

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 オブジェクトの配列です。

書き換えは、デフォルトでは、ファイルシステム(ページおよび/publicファイル)をチェックした後、ダイナミックルートの前に適用されます。この動作は、Next.js のv10.1以降、rewrites関数から配列ではなくオブジェクトを返すことで変更できます。

module.exports = {
  async rewrites() {
    return {
      beforeFiles: [
        // これらの書き換えは、ヘッダ/リダイレクトの後にチェックされます。
        // そして、_next/public ファイルを含むすべてのファイルの前にチェックされます。
        // ページファイルを上書きすることができます
        {
          source: '/some-page',
          destination: '/somewher-else',
          has:[{ type: 'query', key: 'overrideMe' }],
        },
      ],
      afterFiles: [
        // これらの書き換えは、ページ/パブリックファイルの後にチェックされます。
        // がチェックされますが、ダイナミックルートの前に
        {
          source: '/non-existent',
          destination: 'somewher-else',
        },
      ],
      fallback [
        // これらの書き換えは、ページ/パブリックファイルの両方がチェックされた後にチェックされます。
        // とダイナミックルートがチェックされます
        {
          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 ページをレンダリングする前、およびダイナミックルート/すべてのスタティックアセットがチェックされた後に適用されます。

書き換えパラメータ

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

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

デスティネーションでパラメータが使用されている場合は、どのパラメータも自動的にクエリに渡されません。

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

デスティネーションですでにパラメータが使用されている場合でも、destinationでクエリを指定すれば、手動でパラメータを渡すことができます。

module.exports = {
  async rewrites() {
    return [
      {
        source: "/:first/:second",
        destination: "/:first?second=:second",
        // :first パラメーターはデスティネーションで使用されているので、 :second パラメーターは // クエリには自動的に追加されません。
        // 自動的には追加されませんが、手動で追加することは可能です。
        // 上記のように
      },
    ]
  },
}

パスマッチ

パスマッチは許可されています。例えば、/blog/:slug/blog/hello-world にマッチします(ネストしたパスはありません)。

module.exports = {
  async rewrites() {
    return [
      {
        source: "/blog/:slug",
        destination: "/news/:slug", // マッチしたパラメータはdestinationで使用可能です。
      },
    ]
  },
}

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

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

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

正規表現パスのマッチング

例えば、/blog/:slug(\\d{1,})は、/blog/123にマッチしますが、/blog/abcにはマッチしません。

module.exports = {
  async rewrites() {
    return [
      {
        source: "/old-blog/:post(\\d{1,})",
        destination: "/blog/:post", // マッチしたパラメータはdestinationで使用可能です。
      },
    ]
  },
}

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

module.exports = {
  async rewrites() {
    return [
      {
        // これは、`/english(default)/something`というリクエストにマッチします。
        source: "/english\\(default\\)/:slug",
        destination: "/en-us/:slug",
      },
    ]
  },
}

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

ヘッダー、クッキー、クエリの値が一致した場合にのみリライトを適用するには、hasフィールドを使用できます。書き換えが適用されるには、sourceとすべての has アイテムの両方が一致する必要があります。

hasアイテムには、以下のフィールドがあります。

  • type:String - headercookiehostqueryのいずれかでなければなりません。
  • key: String - 選択されたタイプのうち、照合するキーです。
  • value:Stringまたはundefinded - チェックする値で、未定義の場合はどんな値でもマッチします。例えば、first-(?<paramName>.*)first-secondに使用された場合、second:paramNameを持つ宛先で使用可能になります。
module.exports = {
  async rewrites() {
    return [
      // もし、ヘッダ `x-rewrite-me` が存在する場合。
      // このリライトが適用されます
      {
        source: "/:path*",
        has: [
          {
            type: "header",
            key: "x-rewrite-me",
          },
        ],
        destination: "/another-page",
      },
      // ソース、クエリ、クッキーが一致した場合。
      // このリライトが適用されます
      {
        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 への書き換え

事例紹介

Rewrites では、外部の URL に書き換えることができます。これは、Next.js を段階的に導入する際に特に有効です。

module.exports = {
  async rewrites() {
    を返します。
      {
        source: '/blog/:slug',
        destination: 'https://example.com/blog/:slug', // マッチしたパラメータは、デスティネーションで使用できます。
      },
    ]
  },
}

Next.js の段階的な導入

Next.js のルートをすべて確認した後、既存のウェブサイトへのプロキシにフォールバックさせることもできます。

これにより、Next.js に移行するページが増えても、書き換えの設定を変更する必要がありません。

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

インクリメンタル・アダクションに関する追加情報は、こちらのドキュメントをご覧ください。

basePath サポートによる書き換え

basePathサポートを利用した書き換えでは、basePath: falseを書き換えに追加しない限り、各sourcedestinationの前に自動的にbasePathが付けられます。

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 サポートを書き換えで利用する場合、書き換えに locale: false を追加しない限り、設定されたlocaleを処理するために各sourcedestinationは自動的にプレフィックスされます。locale: false を使用した場合、正しくマッチさせるためには、sourcedestinationの前にロケールを付ける必要があります。

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,
      },
      {
        // これは、/(en|fr|de)/(.*)に変換されるので、トップレベルにはマッチしません。
        // `/` や `/fr` のルートには /:path* のようにマッチしません。
        source: "/(.*)",
        destination: "/another",
      },
    ]
  },
}

Discussion

ログインするとコメントできます