レビュー待ち·難易度: 初級·更新: 2026-04-18

Next.js で session cookie を削除してログアウトを実装する

Route Handler で httpOnly session cookie を削除してログアウト処理を実装し、Server Component でのセッション確認と合わせた認証フロー全体を示す例。

nextjsauthentication

対応バージョン

nextjs 15react 19

前提環境

nextjs-cookie-session でセッション Cookie の発行・読み取りを理解していること

概要

cookies().delete() で httpOnly session cookie を削除することでログアウトを実現する。ログアウト後のリダイレクト・Client Component からの呼び出し・複数 Cookie のクリアパターンを示す。

インストール

# 追加インストールは不要

実装

ログアウト Route Handler

// app/api/logout/route.ts
import { cookies } from "next/headers";
import { NextResponse } from "next/server";

export async function POST() {
  const cookieStore = await cookies();

  // session cookie を削除
  cookieStore.delete("session_id");

  // ログインページへリダイレクト
  return NextResponse.redirect(new URL("/login", process.env.NEXT_PUBLIC_BASE_URL ?? "http://localhost:3000"));
}

Server Component でのセッション確認

// app/dashboard/page.tsx
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
import { LogoutButton } from "@/components/LogoutButton";

export default async function DashboardPage() {
  const cookieStore = await cookies();
  const sessionId = cookieStore.get("session_id");

  // セッションがなければログインページへ
  if (!sessionId) {
    redirect("/login");
  }

  return (
    <main className="p-8">
      <h1 className="mb-4 text-2xl font-bold">ダッシュボード</h1>
      <p className="mb-6 text-sm text-gray-600">
        セッション ID: {sessionId.value}
      </p>
      <LogoutButton />
    </main>
  );
}

ログアウトボタン(Client Component)

// components/LogoutButton.tsx
"use client";

import { useRouter } from "next/navigation";
import { useState } from "react";

export function LogoutButton() {
  const router = useRouter();
  const [loading, setLoading] = useState(false);

  async function handleLogout() {
    setLoading(true);
    await fetch("/api/logout", { method: "POST" });
    router.push("/login");
    router.refresh();
  }

  return (
    <button
      onClick={handleLogout}
      disabled={loading}
      className="rounded bg-red-600 px-4 py-2 text-sm text-white hover:bg-red-700 disabled:opacity-50"
    >
      {loading ? "ログアウト中..." : "ログアウト"}
    </button>
  );
}

ログインページ(Cookie の発行)

// app/api/login/route.ts
import { cookies } from "next/headers";
import { NextResponse } from "next/server";

export async function POST(request: Request) {
  const body = await request.json() as { email: string; password: string };

  // 実際のアプリではDB検証・bcrypt.compare() を使う
  if (body.email !== "user@example.com" || body.password !== "password") {
    return NextResponse.json({ error: "認証失敗" }, { status: 401 });
  }

  const sessionId = crypto.randomUUID();
  const cookieStore = await cookies();

  cookieStore.set("session_id", sessionId, {
    httpOnly: true,
    secure: process.env.NODE_ENV === "production",
    sameSite: "lax",
    path: "/",
    maxAge: 60 * 60 * 24, // 24時間
  });

  return NextResponse.json({ message: "ログイン成功" });
}

複数 Cookie を一括クリアする場合

// app/api/logout/route.ts(複数 Cookie 版)
import { cookies } from "next/headers";
import { NextResponse } from "next/server";

const SESSION_COOKIES = ["session_id", "csrf_token", "remember_me"] as const;

export async function POST() {
  const cookieStore = await cookies();

  for (const name of SESSION_COOKIES) {
    cookieStore.delete(name);
  }

  return NextResponse.json({ message: "ログアウトしました" });
}

ポイント

  • cookieStore.delete("session_id") で Cookie を削除する。Set-Cookie: session_id=; Max-Age=0 と同等のレスポンスが返る
  • Client Component から fetch("/api/logout", { method: "POST" }) を呼んだ後に router.refresh() を呼ぶことで Server Component のセッション確認が再実行され、UI が最新状態に更新される
  • router.push("/login")router.refresh() は両方呼ぶ。push だけではページ遷移するが Server Component のキャッシュが残る場合がある
  • nextjs-cookie-session との使い分け: Cookie の発行・読み書きの仕組みはそちらを参照。このサンプルは「ログアウト導線(削除 + リダイレクト + 再検証)」に特化
  • 実際のアプリでは Cookie 削除と合わせてサーバー側の DB セッションも無効化する(例: セッションテーブルから該当レコードを削除)

注意点

nextjs-cookie-session は Cookie の発行・httpOnly 設定・Server Component での読み取りが主題。nextjs-middleware-auth はミドルウェアでのルート保護。これは Cookie 削除によるログアウト処理と、ログアウト後のリダイレクト導線に特化。

関連サンプル