リライト
リライトを使用すると、着信リクエストパスを別の宛先パスにマッピングできます。
リライトは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: [
// これらのリライトは、ヘッダー/リダイレクトの後、
// _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のルートがチェックされる順序は:
- ヘッダーがチェック/適用される
- リダイレクトがチェック/適用される
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パラメータはここでは使用されないため、自動的にクエリに渡されます
},
]
},
}パラメータが宛先で使用される場合、パラメータは自動的にクエリに渡されません。
module.exports = {
async rewrites() {
return [
{
source: '/docs/:path*',
destination: '/:path*', // :pathパラメータはここで使用されるため、自動的にクエリに渡されません
},
]
},
}宛先でパラメータが既に使用されている場合でも、クエリでパラメータを手動で渡すことができます。
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', // マッチしたパラメータは宛先で使用できます
},
]
},
}ワイルドカードパスマッチング
ワイルドカードパスをマッチさせるには、パラメータの後に * を使用します。例えば /blog/:slug* は /blog/a/b/c/d/hello-world にマッチします:
module.exports = {
async rewrites() {
return [
{
source: '/blog/:slug*',
destination: '/news/:slug*', // マッチしたパラメータは宛先で使用できます
},
]
},
}正規表現パスマッチング
正規表現パスをマッチさせるには、パラメータの後に括弧で正規表現を囲みます。例えば /blog/:slug(\\d{1,}) は /blog/123 にマッチしますが、/blog/abc にはマッチしません:
module.exports = {
async rewrites() {
return [
{
source: '/old-blog/:post(\\d{1,})',
destination: '/blog/:post', // マッチしたパラメータは宛先で使用できます
},
]
},
}以下の文字 (, ), {, }, [, ], |, \, ^, ., :, *, +, -, ?, $ は正規表現のパスマッチングに使用されるため、source で特殊でない値として使用する場合は、その前に \\ を追加してエスケープする必要があります:
module.exports = {
async rewrites() {
return [
{
// これは `/english(default)/something` のリクエストにマッチします
source: '/english\\(default\\)/:slug',
destination: '/en-us/:slug',
},
]
},
}ヘッダー、Cookie、クエリのマッチング
リライトを適用するために、ヘッダー、Cookie、クエリの値が has フィールドにマッチするか、missing フィールドにマッチしない場合のみマッチさせることができます。リライトを適用するには、source とすべての has アイテム、すべての missing アイテムがマッチする必要があります。
has と missing アイテムには、以下のフィールドを設定できます:
type:String-header、cookie、host、queryのいずれかでなければなりません。key:String- マッチさせる選択したタイプのキー。value:Stringまたはundefined- チェックする値。未定義の場合、任意の値にマッチします。正規表現のような文字列を使用して値の特定の部分をキャプチャできます。例えば、値first-(?<paramName>.*)をfirst-secondに使用すると、:paramNameを使用して宛先でsecondを使用できます。
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 ルートを外部サイトにリダイレクトする例です。
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 パラメータにも含める必要があります。
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対応を利用する場合、各 source と destination には自動的に basePath が接頭辞として追加されます。ただし、リライトに basePath: false を追加した場合は除きます:
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,
},
]
},
}外部URLへの書き換え
書き換え(Rewrites)を使用すると、外部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 サポートを活用する場合、各 source と destination は自動的に basePath で接頭辞が付けられます。ただし、書き換えに basePath: false を追加する場合を除きます:
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,
},
]
},
}バージョン履歴
| バージョン | 変更点 |
|---|---|
導入時期:v13.3.0 | missing が追加されました。 |
導入時期:v10.2.0 | has が追加されました。 |
導入時期:v9.5.0 | ヘッダーが追加されました。 |