リライト
リライトを使用すると、着信リクエストパスを別の宛先パスにマッピングできます。
リライトは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,
},
]
},
}
i18nサポート付きリライト
i18n
対応を利用する場合、各 source
と destination
には、リライトに locale: false
を追加しない限り、設定された locales
を処理するために自動的に接頭辞が追加されます。locale: false
を使用する場合、正しくマッチさせるためにソースと宛先にロケールを接頭辞として付ける必要があります。
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.0 | missing が追加されました。 |
v10.2.0 | has が追加されました。 |
v9.5.0 | ヘッダーが追加されました。 |