2つのリダイレクト手段
Next.js には用途が異なる 2 つのリダイレクト手段があります。
redirect() | NextResponse.redirect() | |
|---|---|---|
| インポート元 | next/navigation | next/server |
| 使う場所 | Server Component / layout / Server Action / Route Handler | Middleware / Route Handler |
| 動作 | 例外を throw して処理を中断 | レスポンスオブジェクトを返す |
| Client Component | 使えない(useRouter().push() を使う) | 使えない |
redirect()(next/navigation)
Server Component・layout・Server Action・Route Handler で使います。呼び出すと内部的に例外を throw してその場で処理を中断し、HTTP リダイレクトを発行します。
// Server Component での認証チェック
import { redirect } from "next/navigation";
import { cookies } from "next/headers";
export default async function DashboardPage() {
const cookieStore = await cookies();
if (!cookieStore.get("session_id")) {
redirect("/login"); // ここで処理が中断される
}
return <Dashboard />;
}
// Server Action での使い方
"use server";
import { redirect } from "next/navigation";
export async function loginAction(formData: FormData) {
const ok = await authenticate(formData);
if (!ok) return { error: "認証失敗" };
redirect("/dashboard"); // try/catch の外で呼ぶ
}
try/catch の外に出す
redirect() は内部で例外を throw します。try/catch の中で呼ぶとその例外がキャッチされてリダイレクトが発動しません。
// ❌ catch されてリダイレクトされない
try {
redirect("/login");
} catch (e) {
console.error(e); // NEXT_REDIRECT がここに入る
}
// ✅ try/catch の外で呼ぶ
if (!session) redirect("/login");
try {
// ... エラーが起きうる処理
} catch (e) {
return { error: "処理失敗" };
}
NextResponse.redirect()(next/server)
Middleware で使います。NextResponse オブジェクトを返す形で動作し、ルートに到達する前にリダイレクトを発行できます。
// middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) {
const session = request.cookies.get("session_id");
if (!session) {
return NextResponse.redirect(new URL("/login", request.url));
}
return NextResponse.next();
}
Middleware は Edge Runtime で動作するため redirect()(next/navigation)は使えません。NextResponse.redirect() に new URL(path, request.url) を渡して絶対 URL を構成します。
Client Component からは useRouter
Client Component ではどちらも使えません。useRouter().push() を使います。
"use client";
import { useRouter } from "next/navigation";
function LogoutButton() {
const router = useRouter();
return (
<button onClick={() => router.push("/login")}>ログアウト</button>
);
}
認証ユースケースとの対応
| 認証方式 | リダイレクト手段 | 理由 |
|---|---|---|
| Middleware でルート一括保護 | NextResponse.redirect() | Middleware は Edge Runtime のため |
| Layout 保護(ルートグループ) | redirect()(next/navigation) | Server Component / layout で動作するため |
| Server Component 個別確認 | redirect()(next/navigation) | Server Component で動作するため |
| Server Action 後の遷移 | redirect()(next/navigation) | try/catch の外で呼ぶ |
| Client Component のボタン操作 | useRouter().push() | クライアントサイドのナビゲーション |
permanentRedirect()(恒久リダイレクト)
permanentRedirect() は redirect() と同じ場所(Server Component / Server Action / Route Handler)で使えますが、発行する HTTP ステータスが異なります。
redirect() | permanentRedirect() | |
|---|---|---|
| HTTP ステータス | 307(Temporary Redirect) | 308(Permanent Redirect) |
| 用途 | 一時的な転送(認証・条件分岐) | URL 変更が恒久的な場合(旧 URL の移行) |
| ブラウザ・検索エンジン | キャッシュしない | キャッシュする(検索エンジンが index を更新) |
import { permanentRedirect } from "next/navigation";
// /old-slug → /new-slug へ恒久移転
export default async function OldSlugPage() {
permanentRedirect("/new-slug");
}
permanentRedirect() を使う場面:
- サイトリニューアルで URL が変わったとき(旧 URL からの恒久転送)
- コンテンツのスラッグを変更して、旧スラッグを恒久的に新スラッグへ転送するとき
redirect() を使うべき場面(permanentRedirect() を使わない):
- 認証チェックのリダイレクト — セッションが失効しても URL は変わっていないので一時的な転送
- ログアウト後のリダイレクト — 状態に依存する転送はすべて一時的
- 308 はブラウザがキャッシュするため、誤って設定すると意図しないループや取り消しの難しいリダイレクトになる
認証方式の選び分け(Middleware / Layout / Server Component)の詳細は → Next.js 認証方式比較