今回やること
この記事では、ログインしたのにすぐログアウトされる問題を切り分けます。
ログイン不具合は、Set-Cookieが返っているか、ブラウザに保存されたか、次のリクエストで送られたか、サーバー側ストアに残っているか、の4段階で分けると追いやすいです。
画面だけを見ると「ログインできない」に見えますが、HTTP上では原因が分かれます。
まず4段階に分ける
次の順に確認します。
1. ログインレスポンスに Set-Cookie があるか
2. ブラウザに Cookie が保存されたか
3. 次のリクエストに Cookie が付いているか
4. サーバー側でセッションを読めているか
このどこで止まっているかによって、見る場所が変わります。
| 止まる場所 | 主な原因 |
|---|---|
Set-Cookie がない | ログイン処理失敗、レスポンス設定漏れ |
| 保存されない | Secure、SameSite=None、Domain、期限、ブラウザ制限 |
| 送られない | Domain、Path、別オリジン、credentials、SameSite |
| サーバーで読めない | Redis TTL、メモリセッション、Cookie名違い、署名鍵違い |
Step 1: Set-Cookieが返っているか
DevToolsのNetworkでログインAPIまたはログインフォーム送信を開き、Response Headersを見ます。
Set-Cookie: session_id=abc123; Path=/; HttpOnly; Secure; SameSite=Lax
Set-Cookie がない場合、ブラウザの問題ではありません。まずサーバー側がCookieを発行していない可能性があります。
確認すること:
- ログイン認証自体が成功しているか
- レスポンスが
200/302になっているか - エラーレスポンスに進んでいないか
- Cookieをセットする処理が実行されているか
- フレームワークのレスポンスオブジェクトを途中で作り直していないか
たとえば、ログイン成功後に redirect を返す処理で、Cookieをセットしたレスポンスとは別のレスポンスを返してしまうと、Set-Cookie が消えることがあります。
Step 2: Cookieが保存されたか
Set-Cookie が返っていても、ブラウザが保存しないことがあります。
Chrome DevToolsでは、Application > Cookies を見ます。最近のブラウザでは、NetworkのCookieタブに「保存されなかった理由」が出ることもあります。
よくある原因:
| 原因 | 説明 |
|---|---|
Secure 付きでHTTPアクセス | HTTPSではないため保存・送信されない |
SameSite=None なのに Secure がない | 多くのブラウザで拒否される |
Domain が現在のホストと合わない | 不正なドメイン指定として拒否される |
Max-Age=0 になっている | 即削除される |
Expires が過去日時 | 即期限切れになる |
| Cookieサイズが大きすぎる | ブラウザ制限に引っかかる |
| サードパーティCookie制限 | 埋め込みや別サイト文脈で拒否される |
ローカル開発では、Secure が原因になりがちです。http://localhost で試しているのに、本番用の Secure Cookieだけを前提にしていると動かないことがあります。
Step 3: 次のリクエストでCookieが送られているか
保存されていても、送信条件に合わなければCookieは送られません。
DevToolsのNetworkで、ログイン後に /me や /dashboard へアクセスした通信を開き、Request Headersを見ます。
Cookie: session_id=abc123
Cookieがない場合、送信条件を確認します。
| 条件 | 例 |
|---|---|
| Domain | app.example.com 用Cookieは api.example.com に送られない |
| Path | Path=/admin のCookieは /api/me に送られない |
| Secure | HTTP通信には送られない |
| SameSite | クロスサイトPOSTでは送られないことがある |
| credentials | fetch で credentials: "include" が必要な場合がある |
特にSPAでAPIだけ別オリジンの場合、JavaScript側とサーバー側の両方が必要です。
fetch("https://api.example.com/me", {
credentials: "include"
});
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
Cookieが送られない問題を、CORSエラーだけで判断しないことが重要です。CORSはレスポンスを読めるかの制御、Cookie送信はCookie属性とcredentialsの制御です。
Step 4: サーバー側でセッションを読めているか
Cookieが送られているのに未ログインになる場合、サーバー側でセッションを読めていない可能性があります。
見ること:
- Cookie名がサーバー側の期待と一致しているか
- セッションIDの署名検証に失敗していないか
- RedisやDBにセッションが残っているか
- TTLが短すぎないか
- アプリサーバーの秘密鍵が環境ごとに違わないか
- 複数台構成でメモリセッションを使っていないか
よくある構成ミス:
ログイン: App Server A がメモリに session_id を保存
次のアクセス: App Server B に当たる
結果: Bのメモリにはセッションがないため未ログイン
本番で複数台構成にするなら、RedisやDBなど共有セッションストアを使います。
Domainの罠
Cookieの Domain は、広げるための属性です。省略すると、設定元ホストだけに送られるhost-only Cookieになります。
Set-Cookie: session_id=abc; Path=/
app.example.com が発行した場合:
| 送信先 | 送られるか |
|---|---|
app.example.com | 送られる |
api.example.com | 送られない |
example.com | 送られない |
サブドメイン間で共有したい場合:
Set-Cookie: session_id=abc; Domain=example.com; Path=/
ただし、認証Cookieの送信範囲が広がるため、サブドメインの管理状態も含めて考える必要があります。不要に広げない方が安全です。
Pathの罠
Path が狭すぎると、想定したAPIにCookieが送られません。
Set-Cookie: session_id=abc; Path=/login
このCookieは /dashboard や /api/me には送られません。ログイン後に未ログインになる原因になります。
一般的なアプリ全体のログインCookieなら、次のようにします。
Set-Cookie: session_id=abc; Path=/; HttpOnly; Secure; SameSite=Lax
ただし、複数アプリを同じドメイン配下に置いている場合は、Pathを狭める設計もあります。
SameSiteの罠
SameSite=Lax は通常のログインセッションで使いやすいですが、外部サイトをまたぐフローでは注意が必要です。
影響が出やすい場面:
- 外部決済から戻る
- OAuth / OIDC のコールバック
- 別ドメインのフロントエンドからAPIを呼ぶ
- iframe内でログイン状態を使う
- SSOで複数サイトをまたぐ
SameSite=None; Secure が必要な場面もあります。ただし、サードパーティCookie制限の影響を受けるため、「Noneにすれば全部解決」とは考えません。可能なら同一site構成、BFF構成、トップレベル遷移を使う設計も検討します。
重複Cookieの罠
同じ名前のCookieが、DomainやPath違いで複数存在することがあります。
session_id=old; Domain=example.com; Path=/
session_id=new; Domain=app.example.com; Path=/
または:
session_id=old; Path=/api
session_id=new; Path=/
この状態では、サーバーやフレームワークがどちらを読むかで挙動が分かり、ログイン状態が不安定に見えることがあります。
調査時は、Application > Cookies で同名Cookieが複数ないか確認します。ログアウト処理でCookieを削除するときも、発行時と同じ Domain と Path に合わせる必要があります。
すぐ切れる時のチェックリスト
短時間で未ログインになる場合は、次を順番に見ます。
| 確認項目 | 見る場所 |
|---|---|
Max-Age / Expires が短すぎないか | Response Headers |
| Redis TTLが短すぎないか | セッションストア |
| サーバー時計がずれていないか | インフラ |
| デプロイで署名鍵が変わっていないか | 環境変数 |
| ブラウザがCookieを拒否していないか | DevTools Cookies |
| ログアウトAPIが誤って呼ばれていないか | Network |
| 複数タブで別ユーザーのログインが走っていないか | Network / Application |
「ログイン直後だけ成功して、リロードすると切れる」場合は、Cookie保存・送信・セッションストアのいずれかを疑います。
調査メモの書き方
人に相談するときは、次の情報をまとめます。
【症状】
ログイン後 /dashboard に遷移するが、リロードすると /login に戻る
【ログインレスポンス】
Status: 302
Set-Cookie: session_id=...; Path=/; HttpOnly; Secure; SameSite=Lax
【次のリクエスト】
GET /dashboard
Cookie: session_id=... が付いている / 付いていない
【サーバー側】
Redisに session_id がある / ない
TTL: 7200秒
CookieやセッションIDの値そのものは伏せます。値を共有する必要がある場合でも、先頭数文字とハッシュ程度にします。
まとめ
ログインがすぐ切れる問題は、感覚で直すより、HTTPの流れで切り分ける方が早いです。
Set-Cookieが返っているか- ブラウザに保存されたか
- 次のリクエストで送られたか
- サーバー側セッションストアに残っているか
- Domain、Path、Secure、SameSite、credentials、TTLを見る
- 同名Cookieの重複にも注意する
この4段階で見ると、フロント、ブラウザ、Cookie属性、サーバー、インフラのどこに原因があるかを分けられます。