リライト (rewrites)
リライトを使用すると、受信リクエストパスを別の宛先パスにマッピングできます。
リライトはURLプロキシとして機能し、宛先パスをマスクするため、ユーザーがサイト上の位置を変更していないように見えます。一方、リダイレクトは新しいページにルートを変更し、URLの変更を表示します。
リライトを使用するには、next.config.jsのrewritesキーを使用します:
module.exports = {
async rewrites() {
return [
{
source: '/about',
destination: '/',
},
]
},
}リライトはクライアントサイドルーティングに適用され、上記の例では<Link href="/about">にリライトが適用されます。
rewritesは非同期関数で、sourceとdestinationプロパティを持つオブジェクトの配列、または配列のオブジェクト(下記参照)を返すことが期待されます:
source:String- 受信リクエストパスパターンdestination:String- ルーティングしたいパスbasePath:falseまたはundefined- falseの場合、basePathはマッチング時に含まれません。外部リライトのみに使用可能locale:falseまたはundefined- マッチング時にロケールを含めないかどうかhasはtype、key、valueプロパティを持つhasオブジェクトの配列missingはtype、key、valueプロパティを持つmissingオブジェクトの配列
rewrites関数が配列を返す場合、リライトはファイルシステム(ページと/publicファイル)のチェック後、動的ルートの前に適用されます。rewrites関数が特定の形状の配列オブジェクトを返す場合、Next.jsのv10.1以降、この動作を変更し、より細かく制御できます:
module.exports = {
async rewrites() {
return {
beforeFiles: [
// これらのリライトはheaders/redirectsの後にチェックされ
// _next/publicファイルを含むすべてのファイルの前にチェックされるため
// ページファイルの上書きが可能
{
source: '/some-page',
destination: '/somewhere-else',
has: [{ type: 'query', key: 'overrideMe' }],
},
],
afterFiles: [
// これらのリライトはpages/publicファイルのチェック後
// 動的ルートの前にチェックされる
{
source: '/non-existent',
destination: '/somewhere-else',
},
],
fallback: [
// これらのリライトはpages/publicファイルと
// 動的ルートのチェック後にチェックされる
{
source: '/:path*',
destination: `https://my-old-site.com/:path*`,
},
],
}
},
}知っておくと良い:
beforeFilesのリライトは、ソースにマッチした後すぐにファイルシステム/動的ルートをチェックせず、すべてのbeforeFilesがチェックされるまで続きます。
Next.jsのルートチェック順序は以下の通りです:
- headersがチェック/適用される
- redirectsがチェック/適用される
beforeFilesリライトがチェック/適用される- publicディレクトリの静的ファイル、
_next/staticファイル、非動的ページがチェック/提供される afterFilesリライトがチェック/適用され、これらのリライトのいずれかがマッチすると、各マッチ後に動的ルート/静的ファイルがチェックされるfallbackリライトがチェック/適用される。これらは404ページのレンダリング前、動的ルート/すべての静的アセットのチェック後に適用されます。getStaticPathsでfallback: true/'blocking'を使用している場合、next.config.jsで定義されたfallbackrewritesは実行されません。
リライトパラメータ
リライトでパラメータを使用する場合、パラメータがdestinationで使用されていない場合、デフォルトでクエリに渡されます。
module.exports = {
async rewrites() {
return [
{
source: '/old-about/:path*',
destination: '/about', // :pathパラメータはここで使用されていないため、自動的にクエリに渡されます
},
]
},
}パラメータがdestinationで使用されている場合、パラメータは自動的にクエリに渡されません。
module.exports = {
async rewrites() {
return [
{
source: '/docs/:path*',
destination: '/:path*', // :pathパラメータはここで使用されているため、自動的にクエリに渡されません
},
]
},
}destinationですでにパラメータが使用されている場合でも、destinationでクエリを指定することで、手動でパラメータを渡すことができます。
module.exports = {
async rewrites() {
return [
{
source: '/:first/:second',
destination: '/:first?second=:second',
// :firstパラメータがdestinationで使用されているため、:secondパラメータは
// 自動的にクエリに追加されませんが、上記のように手動で追加できます
},
]
},
}知っておくと良い: Automatic Static Optimizationまたはプリレンダリングからの静的ページのリライトパラメータは、ハイドレーション後にクライアント側で解析され、クエリで提供されます。
パスマッチング
/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フィールドを使用します。またはmissingフィールドを使用してマッチしない場合に適用します。リライトが適用されるためには、sourceとすべてのhasアイテムがマッチし、すべてのmissingアイテムがマッチしない必要があります。
hasとmissingアイテムには以下のフィールドがあります:
type:String-header、cookie、host、またはqueryのいずれかでなければなりませんkey:String- マッチ対象の選択されたタイプのキーvalue:Stringまたはundefined- チェックする値。undefinedの場合、任意の値がマッチします。値の特定の部分をキャプチャするために正規表現のような文字列を使用できます。例えば、first-(?<paramName>.*)という値をfirst-secondに使用すると、secondはdestinationで:paramNameとして使用可能になります
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',
},
// source、クエリ、クッキーがマッチした場合、
// このリライトが適用されます
{
source: '/specific/:path*',
has: [
{
type: 'query',
key: 'page',
// 値が提供され、名前付きキャプチャグループ(例:(?<page>home))が
// 使用されていないため、page値はdestinationで利用できません
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ルートを外部サイトにリダイレクトするリライトの例です。
module.exports = {
async rewrites() {
return [
{
source: '/blog',
destination: 'https://example.com/blog',
},
{
source: '/blog/:slug',
destination: 'https://example.com/blog/:slug', // マッチしたパラメータはdestinationで使用可能
},
]
},
}trailingSlash: trueを使用している場合、sourceパラメータに末尾のスラッシュを挿入する必要もあります。宛先サーバーも末尾のスラッシュを期待している場合、destinationパラメータにも含める必要があります。
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に移行する際にリライト設定を変更する必要がありません
module.exports = {
async rewrites() {
return {
fallback: [
{
source: '/:path*',
destination: `https://custom-routes-proxying-endpoint.vercel.app/:path*`,
},
],
}
},
}basePathサポートのあるリライト
basePathサポートをリライトと併用する場合、basePath: falseをリライトに追加しない限り、各sourceとdestinationに自動的にbasePathがプレフィックスされます:
module.exports = {
basePath: '/docs',
async rewrites() {
return [
{
source: '/with-basePath', // 自動的に/docs/with-basePathになる
destination: '/another', // 自動的に/docs/anotherになる
},
{
// basePath: falseが設定されているため、/without-basePathに/docsを追加しない
// 注意:これは`destination: '/another'`のような内部リライトには使用できない
source: '/without-basePath',
destination: 'https://example.com',
basePath: false,
},
]
},
}i18nサポートのあるリライト
i18nサポートをリライトと併用する場合、locale: falseをリライトに追加しない限り、各sourceとdestinationは設定されたlocalesを処理するために自動的にプレフィックスされます。locale: falseを使用する場合、正しくマッチさせるためにsourceとdestinationにロケールをプレフィックスする必要があります。
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`がdefaultLocaleであるため、'/'にマッチ
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.0 | missing追加 |
v10.2.0 | has追加 |
v9.5.0 | ヘッダーサポート追加 |