Sponsor
ChatHubChatHub Use GPT-4, Gemini, Claude 3.5 and more chatbots side-by-side
ここをクリック
Menu

リダイレクト

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

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

redirect 関数

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

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

app/actions.ts
TypeScript
'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(一時的リダイレクト)ステータスコードを返します。Server Actionで使用する場合は、POSTリクエストの結果として成功ページにリダイレクトする際によく使われる303(See Other)を返します。
  • redirectは内部的にエラーをスローするため、try/catchブロックの外で呼び出す必要があります。
  • redirectはレンダリングプロセス中にClient Componentsで呼び出すことができますが、イベントハンドラーでは呼び出せません。代わりにuseRouterフックを使用できます。
  • redirectは絶対URLも受け付け、外部リンクへのリダイレクトに使用できます。
  • レンダリングプロセスの前にリダイレクトする場合は、next.config.jsまたはMiddlewareを使用してください。

詳細についてはredirect APIリファレンスを参照してください。

permanentRedirect 関数

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

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

app/actions.ts
TypeScript
'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() フック

Client Componentのイベントハンドラー内でリダイレクトする必要がある場合は、useRouterフックのpushメソッドを使用できます。例:

app/page.tsx
TypeScript
'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.jsredirects

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

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

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

next.config.ts
TypeScript
import type { NextConfig } from 'next'
 
const nextConfig: NextConfig = {
  async redirects() {
    return [
      // 基本的なリダイレクト
      {
        source: '/about',
        destination: '/',
        permanent: true,
      },
      // ワイルドカードパスマッチング
      {
        source: '/blog/:slug',
        destination: '/news/:slug',
        permanent: true,
      },
    ]
  },
}
 
export default nextConfig

詳細についてはredirects APIリファレンスを参照してください。

補足:

  • redirectspermanentオプションで307(一時的リダイレクト)または308(恒久的リダイレクト)ステータスコードを返すことができます。
  • redirectsはプラットフォームによって制限がある場合があります。例えば、Vercelでは1,024個のリダイレクトに制限があります。大量のリダイレクト(1000以上)を管理するには、Middlewareを使用してカスタムソリューションを作成することを検討してください。詳細は大規模なリダイレクトの管理を参照してください。
  • redirectsはMiddlewareの前に実行されます。

MiddlewareのNextResponse.redirect

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

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

middleware.ts
TypeScript
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*',
}

補足:

  • Middlewareはnext.config.jsredirects、レンダリングのに実行されます。

詳細についてはMiddlewareのドキュメントを参照してください。

大規模なリダイレクトの管理(高度)

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

そのためには、以下を検討する必要があります:

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

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

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

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

以下のデータ構造を考えてみましょう:

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

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

middleware.ts
TypeScript
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など、高速な読み取りに最適化されたデータベースを使用する。
  • より大きなリダイレクトファイルやデータベースを読み取る前に、リダイレクトが存在するかどうかを効率的にチェックするためにBloom filterなどのデータ検索戦略を使用する。

前の例を考慮して、生成されたBloom filterファイルをMiddlewareにインポートし、受信リクエストのパス名がBloom filterに存在するかどうかをチェックできます。

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

middleware.ts
TypeScript
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ファイルからBloom filterを初期化
const bloomFilter = ScalableBloomFilter.fromJSON(GeneratedBloomFilter as any)
 
export async function middleware(request: NextRequest) {
  // 受信リクエストのパスを取得
  const pathname = request.nextUrl.pathname
 
  // パスがBloom filterに存在するかチェック
  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/api/redirects/route.ts
TypeScript
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('Bad Request', { status: 400 })
  }
 
  // redirects.jsonファイルからリダイレクトエントリを取得
  const redirect = (redirects as Record<string, RedirectEntry>)[pathname]
 
  // Bloom filterの偽陽性に対応
  if (!redirect) {
    return new Response('No redirect', { status: 400 })
  }
 
  // リダイレクトエントリを返す
  return NextResponse.json(redirect)
}

補足:

  • Bloom filterを生成するには、bloom-filtersなどのライブラリを使用できます。
  • 悪意のあるリクエストを防ぐために、Route Handlerに対するリクエストを検証する必要があります。