メインコンテンツまでスキップ

リダイレクト

Next.jsでリダイレクトを処理する方法はいくつかあります。このページでは、利用可能な各オプション、ユースケース、および大量のリダイレクトを管理する方法について説明します。

API目的使用場所ステータスコード
redirectミューテーションまたはイベント後にユーザーをリダイレクトするServer Components、Server Actions、Route Handlers307(仮)または303(Server Action)
permanentRedirectミューテーションまたはイベント後にユーザーをリダイレクトするServer Components、Server Actions、Route Handlers308(永続)
useRouterクライアントサイドのナビゲーションを実行するClient Componentsのイベントハンドラーで該当なし
next.config.js内のredirectsパスに基づいて受信リクエストをリダイレクトするnext.config.jsファイル307(仮)または308(永続)
NextResponse.redirect条件に基づいて受信リクエストをリダイレクトするミドルウェア任意

redirect 関数

redirect 関数を使用すると、ユーザーを別のURLにリダイレクトすることができます。redirectServer ComponentsRoute Handlers、およびServer Actionsで呼び出すことができます。

redirectは、ミューテーションまたはイベントの後によく使用されます。たとえば、投稿を作成する場合です:

app/actions.tsx
'use server'

import { redirect } from 'next/navigation'
import { revalidatePath } from 'next/cache'

export async function createPost(id: string) {
try {
// データベースを呼び出す
} catch (error) {
// エラーを処理する
}

revalidatePath('/posts') // キャッシュされた投稿を更新する
redirect(`/post/${id}`) // 新しい投稿ページに移動する
}

参考情報

  • redirectはデフォルトで307(Temporary Redirect)ステータスコードを返します。Server Actionで使用されるとき、通常はPOSTリクエストの結果として成功ページにリダイレクトするために使用される303(See Other)を返します。
  • redirectは内部でエラーをスローするため、try/catchブロックの外で呼び出す必要があります。
  • redirectはクライアントコンポーネントのレンダリングプロセス中に呼び出すことができますが、イベントハンドラーでは呼び出せません。useRouterフックを代わりに使用できます。
  • redirectは絶対URLも受け入れ、外部リンクにリダイレクトするために使用できます。
  • レンダープロセスの前にリダイレクトしたい場合は、next.config.jsまたはMiddlewareを使用してください。

redirect APIリファレンスを参照して、さらに情報をご覧ください。

permanentRedirect 関数

permanentRedirect 関数は、ユーザーを別のURLに永久にリダイレクトすることができます。permanentRedirectServer ComponentsRoute Handlers、およびServer Actionsで呼び出すことができます。

permanentRedirectは、エンティティの正規URLが変更されるミューテーションまたはイベントの後によく使用されます。たとえば、ユーザーがユーザー名を変更した後にプロフィールURLを更新する場合です:

app/actions.ts
'use server'

import { permanentRedirect } from 'next/navigation'
import { revalidateTag } from 'next/cache'

export async function updateUsername(username: string, formData: FormData) {
try {
// データベースを呼び出す
} catch (error) {
// エラーを処理する
}

revalidateTag('username') // ユーザー名のすべての参照を更新する
permanentRedirect(`/profile/${username}`) // 新しいユーザープロフィールに移動する
}

参考情報

  • permanentRedirectはデフォルトで308(恒久的リダイレクト)ステータスコードを返します;
  • permanentRedirectは絶対URLも受け入れ、外部リンクにリダイレクトするために使用できます;
  • レンダープロセスの前にリダイレクトしたい場合は、next.config.jsまたはMiddlewareを使用してください;

permanentRedirect APIリファレンスを参照して、さらに情報をご覧ください。

useRouter() フック

クライアントコンポーネントのイベントハンドラー内でリダイレクトが必要な場合、useRouterフックのpushメソッドを使用できます。例えば:

app/page.tsx
'use client'

import { useRouter } from 'next/navigation'

export default function Page() {
const router = useRouter()

return (
<button type="button" onClick={() => router.push('/dashboard')}>
Dashboard
</button>
)
}

参考情報

  • プログラムでユーザーをナビゲートする必要がない場合は、<Link>コンポーネントを使用するべきです。

useRouter APIリファレンスを参照して、さらに情報をご覧ください。

next.config.js内のredirects

next.config.jsファイル内のredirectsオプションを使用すると、受信リクエストのパスを別の宛先パスにリダイレクトできます。これは、ページのURL構造を変更した場合や、事前に既知のリダイレクトのリストがある場合に便利です。

redirectsは、パスヘッダー、cookie、クエリのマッチングをサポートし、受信リクエストに基づいてユーザーをリダイレクトする柔軟性を提供します。

redirectsを使用するには、next.config.jsファイルにオプションを追加します:

next.config.js
module.exports = {
async redirects() {
return [
// 基本的なリダイレクト
{
source: '/about',
destination: '/',
permanent: true,
},
// ワイルドカードパスマッチング
{
source: '/blog/:slug',
destination: '/news/:slug',
permanent: true,
},
]
},
}

redirects APIリファレンスを参照して、さらに情報をご覧ください。

参考情報

  • redirectsは、permanentオプションを使用して307(Temporary Redirect)または308(Permanent Redirect)ステータスコードを返すことができます;
  • プラットフォームによっては、redirectsに制限がある場合があります。たとえば、Vercelでは1,024件のリダイレクトの制限があります。大量のリダイレクト(1000件以上)を管理するには、Middlewareを使用したカスタムソリューションを作成することを検討してください。規模に応じたリダイレクト管理については、managing redirects at scaleを参照してください;
  • redirectsはミドルウェアの前に実行されます;

NextResponse.redirect in Middleware

ミドルウェアを使用すると、リクエストが完了する前にコードを実行できます。そして、受信リクエストに基づいてNextResponse.redirectを使用して別のURLにリダイレクトします。これは、条件(例:認証、セッション管理など)に基づいてユーザーをリダイレクトする場合や、大量のリダイレクトがあります:

たとえば、ユーザーが認証されていない場合に/loginページにリダイレクトするには:

middleware.ts
import { NextResponse, NextRequest } from 'next/server'
import { authenticate } from 'auth-provider'

export function middleware(request: NextRequest) {
const isAuthenticated = authenticate(request)

// ユーザーが認証されている場合は、通常通り続行します
if (isAuthenticated) {
return NextResponse.next()
}

// 認証されていない場合は、ログインページにリダイレクトします
return NextResponse.redirect(new URL('/login', request.url))
}

export const config = {
matcher: '/dashboard/:path*',
}

参考情報

  • ミドルウェアは、next.config.jsredirects後に実行され、レンダリングの前に実行されます;

Middlewareのドキュメントを参照して、さらに情報をご覧ください。

規模に応じたリダイレクト管理(高度)

大量のリダイレクト(1000件以上)を管理するために、ミドルウェアを使用してカスタムソリューションを作成することを検討してください。これにより、アプリケーションを再デプロイすることなく、プログラムでリダイレクトを処理できます。

これを行うには、以下を考慮する必要があります:

  1. リダイレクトマップの作成と保存
  2. データ検索パフォーマンスの最適化

Next.jsの例:以下の推奨事項の実装については、Middleware with Bloom filterの例を参照してください。

1. リダイレクトマップの作成と保存

リダイレクトマップは、データベース(通常はキー・バリュー・ストア)またはJSONファイルに保存できるリダイレクトのリストです。

次のデータ構造を考慮してください:

{
"/old": {
"destination": "/new",
"permanent": true
},
"/blog/post-old": {
"destination": "/blog/post-new",
"permanent": true
}
}

Middlewareで、VercelのEdge ConfigRedisなどのデータベースから読み取り、受信リクエストに基づいてユーザーをリダイレクトすることができます:

middleware.ts
import { NextResponse, NextRequest } from 'next/server'
import { get } from '@vercel/edge-config'

type RedirectEntry = {
destination: string
permanent: boolean
}

export async function middleware(request: NextRequest) {
const pathname = request.nextUrl.pathname
const redirectData = await get(pathname)

if (redirectData && typeof redirectData === 'string') {
const redirectEntry: RedirectEntry = JSON.parse(redirectData)
const statusCode = redirectEntry.permanent ? 308 : 307
return NextResponse.redirect(redirectEntry.destination, statusCode)
}

// リダイレクトが見つからなかった場合、そのまま継続します
return NextResponse.next()
}

2. データ検索パフォーマンスの最適化

大規模データセットをすべての受信リクエストのために読み取ることは遅く、費用がかかります。データ検索パフォーマンスを最適化する方法は2つあります:

  • Vercel Edge ConfigRedisなどの、高速読み取りに最適化されたデータベースを使用します;
  • ブルームフィルターのようなデータ検索戦略を使用して、リダイレクトが存在するかどうかを効率的にチェックし、より大きなリダイレクトファイルやデータベースを読み取る前に行います;

前述の例では、生成されたブルームフィルターファイルをミドルウェアにインポートし、受信リクエストのパス名がブルームフィルターに存在するかどうかを確認します。

存在する場合は、要求を Route Handlerに転送し、要求が実際に存在するかをチェックし、ユーザーを適切なURLにリダイレクトします。これにより、ミドルウェアに大量のリダイレクトファイルをインポートすることを避けることができ、すべての受信リクエストが遅くなるのを防ぎます。

middleware.ts
import { NextResponse, NextRequest } from 'next/server'
import { ScalableBloomFilter } from 'bloom-filters'
import GeneratedBloomFilter from './redirects/bloom-filter.json'

type RedirectEntry = {
destination: string
permanent: boolean
}

// 生成されたJSONファイルからブルームフィルターを初期化する
const bloomFilter = ScalableBloomFilter.fromJSON(GeneratedBloomFilter as any)

export async function middleware(request: NextRequest) {
// 受信リクエストのパスを取得する
const pathname = request.nextUrl.pathname

// パスがブルームフィルターにあるかチェックする
if (bloomFilter.has(pathname)) {
// パス名をRoute Handlerに転送する
const api = new URL(
`/api/redirects?pathname=${encodeURIComponent(request.nextUrl.pathname)}`,
request.nextUrl.origin
)

try {
// Route Handlerからリダイレクトデータを取得する
const redirectData = await fetch(api)

if (redirectData.ok) {
const redirectEntry: RedirectEntry | undefined =
await redirectData.json()

if (redirectEntry) {
// ステータスコードを決定する
const statusCode = redirectEntry.permanent ? 308 : 307

// 宛先にリダイレクトする
return NextResponse.redirect(redirectEntry.destination, statusCode)
}
}
} catch (error) {
console.error(error)
}
}

// リダイレクトが見つからなかった場合、そのままリクエストを続行します
return NextResponse.next()
}

その後、Route Handlerでは:

app/redirects/route.ts
import { NextRequest, NextResponse } from 'next/server'
import redirects from '@/app/redirects/redirects.json'

type RedirectEntry = {
destination: string
permanent: boolean
}

export function GET(request: NextRequest) {
const pathname = request.nextUrl.searchParams.get('pathname')
if (!pathname) {
return new Response('不正なリクエスト', { status: 400 })
}

// redirects.jsonファイルからリダイレクトエントリを取得する
const redirect = (redirects as Record<string, RedirectEntry>)[pathname]

// ブルームフィルターの誤検知に対応する
if (!redirect) {
return new Response('リダイレクトなし', { status: 400 })
}

// リダイレクトエントリを返す
return NextResponse.json(redirect)
}

参考情報

  • ブルームフィルターを生成するために、bloom-filtersのようなライブラリを使用できます;
  • 悪意のあるリクエストを防止するために、Route Handlerに対して行われるリクエストを検証する必要があります;