Next.js の App Router では、「ログインしていなければログインページへ飛ばす」「投稿を保存したら一覧ページへ移動する」といった処理を、サーバー側で行いたい場面があります。このときに使うのが redirect と permanentRedirect という関数です。どちらも next/navigation から呼び出し、Server Component や Server Actions の中で別のページへリダイレクトさせます。この記事では、2つの関数の基本的な使い方、一時的なリダイレクトと恒久的なリダイレクトの違い、そして呼び出したあとに処理が止まる独特の仕組みまで、初心者〜中級者向けに解説します。
目次
redirect の基本的な使い方
redirect は、引数に渡したパスへリダイレクトする関数です。Server Component の中で条件分岐と組み合わせ、「特定の条件のときだけ別ページへ飛ばす」といった使い方をします。次は、ユーザー情報が取得できなければログインページへリダイレクトする例です。
import { redirect } from "next/navigation";
import { getUser } from "@/lib/auth";
export default async function DashboardPage() {
const user = await getUser();
if (!user) {
// 未ログインならログインページへ飛ばす
redirect("/login");
}
// redirect の後はここに到達しない
return <h1>{user.name} さんのダッシュボード</h1>;
}
user が取得できなければ redirect("/login") を呼び、ログインページへ遷移します。ここで重要なのが、redirect を呼び出したあとのコードは実行されないという点です。そのため、redirect の後で user.name を参照していても、リダイレクトが発生した場合はそこに到達しません。return を書く必要すらない、と覚えておくと分かりやすいでしょう。
redirect が処理を中断する仕組み
redirect が「呼び出し後のコードを実行しない」のは、内部で特別なエラーを throw(送出)しているからです。この仕組みには実用上の注意点があります。redirect を try/catch の中で呼ぶと、その内部エラーを catch が拾ってしまい、リダイレクトが正しく働かなくなることがあります。
// 悪い例:redirect を try の中で呼んでいる
try {
const data = await save();
redirect("/done"); // 内部エラーが catch に捕まってしまう
} catch (e) {
console.error(e);
}
// 良い例:処理が成功したあと、try/catch の外で呼ぶ
let data;
try {
data = await save();
} catch (e) {
console.error(e);
}
redirect("/done");
redirect は try/catch の外で呼ぶのが基本です。どうしても try の中で使いたい場合は、catch でエラーを握りつぶさず再度 throw するなどの配慮が必要になりますが、シンプルに保つなら「データ処理が終わってから、外でリダイレクトする」構成にするのが確実です。
Server Actions の中でリダイレクトする
redirect は Server Actions とも相性がよく、フォーム送信後の遷移によく使われます。データを保存したあと、一覧ページなどへ移動させる流れです。
"use server";
import { redirect } from "next/navigation";
import { createPost } from "@/lib/posts";
export async function addPost(formData: FormData) {
const title = formData.get("title") as string;
const post = await createPost(title);
// 保存できたら詳細ページへ遷移
redirect(`/posts/${post.id}`);
}
投稿を作成したあと、その投稿の詳細ページへ redirect しています。フォーム送信の処理がサーバーで完結し、成功時にそのまま次のページへ送れるため、クライアント側で遷移処理を書く必要がありません。
redirect と permanentRedirect の違い
permanentRedirect は使い方こそ redirect と同じですが、返す HTTP ステータスコードが異なります。この違いは、検索エンジンやブラウザに「この移動は一時的か、恒久的か」を伝えるうえで重要です。
| 関数 | ステータス | 意味・使いどころ |
|---|---|---|
redirect | 307(一時的) | ログイン誘導やフォーム送信後など、状況に応じた一時的な遷移 |
permanentRedirect | 308(恒久的) | ページのURLが恒久的に変わった場合。検索エンジンに移転を伝えたいとき |
「未ログインだからログインへ」のように、その時々の状況で飛ばす一時的な遷移には redirect(307)を使います。一方、「このページは新しい URL に引っ越した」という恒久的な移転を伝えたいときは permanentRedirect(308)を使います。恒久リダイレクトはブラウザや検索エンジンにキャッシュされやすいため、本当に URL が変わらないケースにだけ使うのが安全です。
import { permanentRedirect } from "next/navigation";
export default function OldPage() {
// 旧URLを新URLへ恒久的に移転(308)
permanentRedirect("/new-page");
}
クライアント側の遷移との使い分け
redirect・permanentRedirect はサーバー側(Server Component、Server Actions、Route Handlers)で使う関数です。ボタンのクリックなど、ブラウザ上のユーザー操作をきっかけに遷移したい場合は、Client Component で useRouter の push を使います。「サーバーで条件を判定して飛ばすなら redirect、クライアントの操作で飛ばすなら useRouter」と整理しておくと迷いません。
なお、リダイレクト先には内部のパスだけでなく外部の URL(https:// から始まる完全な URL)も指定できます。外部サイトへ飛ばしたいときにも同じ関数が使えます。
まとめ
redirect と permanentRedirect は、Server Component や Server Actions などサーバー側の処理から別ページへリダイレクトさせる関数です。redirect は一時的な遷移(307)、permanentRedirect は恒久的な移転(308)を表し、用途に応じて使い分けます。どちらも内部でエラーを送出して以降の処理を中断するため、redirect の後のコードは実行されず、try/catch の中で呼ぶと意図せずエラーが捕まる点に注意が必要です。ユーザー操作による遷移はクライアント側の useRouter を使うことも踏まえ、サーバー側の条件分岐によるリダイレクトに活用してみてください。