リライト (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
で定義されたfallback
rewrites
は実行されません。
リライトパラメータ
リライトでパラメータを使用する場合、パラメータが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 | ヘッダーサポート追加 |