ログアウト時に何が起きるか - セッション失効の実務

中級 | 12分 で読める | 2026.06.27

公式ドキュメント

ログアウトは「画面をログイン前に戻す」だけではない

ログアウト処理では、画面遷移だけでなく、認証状態を無効にする必要があります。

Cookieセッションのログアウトは、サーバー側セッションの削除と、ブラウザ側Cookieの削除をセットで考えます。

どちらか片方だけだと、ログアウトしたように見えても、状態が残ることがあります。

基本のログアウト処理

典型的な流れは次の通りです。

1. ブラウザがログアウトAPIへPOSTする
2. サーバーが現在のsession_idを読む
3. セッションストアから該当セッションを削除する
4. Set-Cookieでブラウザ側Cookieを期限切れにする
5. ログイン画面やトップページへ遷移する

HTTPでは次のようになります。

POST /logout HTTP/1.1
Cookie: session_id=abc123

レスポンス:

HTTP/1.1 302 Found
Set-Cookie: session_id=; Path=/; Max-Age=0; HttpOnly; Secure; SameSite=Lax
Cache-Control: no-store
Location: /login

Max-Age=0 は、Cookieを即座に期限切れにする指定です。

サーバー側セッションを削除する理由

Cookieを消すだけでは不十分です。

ブラウザからCookieが消えても、サーバー側にセッションが残っていると、何らかの理由で同じセッションIDが再送されたときに受け付けられる可能性があります。

そのため、ログアウト時はセッションストアから削除します。

DELETE session:abc123

または、監査用に行を残す場合でも、無効状態にします。

{
  "sessionIdHash": "8b1a9953",
  "userId": "user_001",
  "revokedAt": "2026-06-27T10:30:00.000Z",
  "reason": "user_logout"
}

セッションIDそのものを監査ログに残すのは避けます。残す場合はハッシュ化や短い識別子にします。

Cookie削除は発行時と条件を合わせる

Cookie削除でよくあるミスは、発行時と違う DomainPath で削除しようとすることです。

発行:

Set-Cookie: session_id=abc; Domain=example.com; Path=/; HttpOnly; Secure; SameSite=Lax

削除:

Set-Cookie: session_id=; Domain=example.com; Path=/; Max-Age=0; HttpOnly; Secure; SameSite=Lax

削除時に DomainPath がずれていると、別Cookieを削除しようとしてしまい、元のCookieが残ります。

特に次の構成では注意します。

  • app.example.comapi.example.com を使っている
  • Domain=example.com を付けている
  • /admin/api などPathを分けている
  • 過去の実装で同名Cookieが複数ある

DevToolsのApplication > Cookiesで、同名Cookieが残っていないか確認します。

GETログアウトを避ける

ログアウトは状態変更です。基本は POST にします。

避けたい例:

<a href="/logout">ログアウト</a>

GETログアウトは、リンクのプレビュー、クローラー、画像読み込み、外部サイトからのリンクなどで意図せず発火する可能性があります。

望ましい例:

<form action="/logout" method="post">
  <button type="submit">ログアウト</button>
</form>

ログアウトは金銭的被害につながりにくいこともありますが、ユーザー体験やセッション管理上の状態変更です。CSRF対策、SameSite、Origin検証の方針に沿って扱います。

複数タブの挙動

ログアウトで見落としやすいのが複数タブです。

タブA: /dashboard を表示
タブB: /settings を表示
タブBでログアウト
タブAは見た目だけ古い画面のまま

このとき、タブAで次にAPIを呼ぶと 401 になり、ログイン画面へ戻るのが自然です。ただし、タブAの画面がブラウザキャッシュで残ることがあります。

対策:

  • 個人ページに Cache-Control: no-store を付ける
  • APIが 401 を返したらフロント側でログイン画面へ遷移する
  • 複数タブへ通知したい場合は BroadcastChannel やstorage eventを使う
  • 重要操作前にはサーバー側で必ず再確認する

フロント側の表示を消すだけでは認可になりません。重要なのは、サーバー側でセッションが無効になっていることです。

戻るボタンで画面が見える問題

ログアウト後にブラウザの戻るボタンでマイページが見えることがあります。

この場合、サーバーがログイン済みと判断しているとは限りません。ブラウザが以前のHTMLをキャッシュ表示しているだけのことがあります。

個人ページでは次を検討します。

Cache-Control: no-store

古い互換性が必要な場合は、追加で次を使うこともあります。

Pragma: no-cache
Expires: 0

ただし、最終的な保護はサーバー側認可です。戻るボタンで見える画面からAPIを叩いたときに、無効なセッションなら拒否される必要があります。

全端末ログアウト

「この端末だけログアウト」と「全端末ログアウト」は別です。

操作やること
この端末だけログアウト現在のsession_idだけ削除
全端末ログアウトそのユーザーの全セッションを削除
パスワード変更後の失効既存セッションを削除、または再認証要求
管理者による強制ログアウト対象ユーザーのセッションを失効

セッションストアに userIdsessionId の対応を持たせておくと、全端末ログアウトを実装しやすくなります。

user_sessions:user_001 = [session_a, session_b, session_c]

全端末ログアウト:

DELETE session_a
DELETE session_b
DELETE session_c
DELETE user_sessions:user_001

RDBで管理する場合は、revoked_atexpires_at を持つセッションテーブルにすると、監査や管理画面と相性がよくなります。

JWTログアウトとの違い

Cookieセッションでは、サーバー側セッションを削除すれば即時失効しやすいです。

JWTでは、アクセストークン自体が有効期限を持つため、純粋なステートレス構成では発行済みトークンを即時に止めにくくなります。

観点CookieセッションJWT
状態の本体サーバー側トークン内
ログアウトセッション削除クライアント側削除だけでは不十分
即時失効しやすいブラックリスト等が必要
全端末ログアウトセッション一覧を削除tokenVersion等が必要

JWTを使う場合でも、refresh tokenをサーバー側で管理したり、ブラックリストやtokenVersionを使ったりすると、結局何らかの状態管理が入ります。

ログアウトのテスト観点

最低限、次を確認します。

  • ログアウト後、保護ページへ直接アクセスするとログイン画面へ戻る
  • ログアウト後、APIが 401 を返す
  • Cookieがブラウザから削除される
  • セッションストアから該当セッションが消える
  • 戻るボタンで表示されても重要APIは通らない
  • 複数タブでログアウト後、別タブの操作が拒否される
  • 全端末ログアウトで他端末も失効する
  • ログアウト処理を複数回呼んでも壊れない

ログアウト処理は冪等にしておくと扱いやすいです。すでにセッションがない状態でログアウトAPIを呼んでも、成功扱いでログイン画面へ戻せばよい場面が多いです。

まとめ

ログアウトは、表示を切り替える処理ではなく、セッションを失効させる処理です。

  • サーバー側セッションを削除する
  • ブラウザ側Cookieを期限切れにする
  • Cookie削除時は発行時のDomain/Pathを合わせる
  • GETログアウトは避け、POSTで扱う
  • 複数タブや戻るボタンはキャッシュとAPI認可を分けて考える
  • 全端末ログアウトにはセッション一覧管理が必要
  • JWTでは即時失効に追加設計が必要

「ログアウトしたように見える」ではなく、「古い認証情報ではサーバーが処理を受け付けない」状態にすることが大切です。

参考リソース

次に読む記事

← 一覧に戻る
PR
PR
PR
PR