第17章:Cookieとセッションの地雷を避ける🍪💣
この章は「リバースプロキシで複数アプリを同居させた瞬間に、ログインが飛ぶ/戻る/消える😇」を先に潰す回だよ〜🛠️✨ 結論から言うと、事故はだいたい SameSite / Secure / Domain / Path / “HTTPSだと思われてない” あたりで起きます🍪🔍 (ここを押さえると、18章の「パス方式」や19章の「サブドメイン方式」がめちゃ安定する👍)
0) まず “何が起きてる世界” なの?🗺️
イメージはこれ👇(入口=リバプロ、奥=複数アプリ)
ブラウザ
│ https://front.localhost ← 入口1個(リバプロ)
▼
[ Reverse Proxy ]
│ │
▼ ▼
Front App API App
(3000) (4000)
- Cookieは、サーバが
Set-Cookieというレスポンスヘッダで「ブラウザに保存してね🍪」って渡すやつ - ブラウザは条件が合うと、次回リクエストで
Cookie:ヘッダとして送り返す📨 - でも、条件(属性)が1個でもズレると 保存されない/送られない が発生する😇 (MDN Web Docs)
1) 今日のゴール🎯✨
✅ ゴール
- ローカルでも「ログイン維持」が安定する🍪🔒
- 複数アプリ同居でも「別アプリのCookieが邪魔しない」🧹
- OAuthや別ドメイン連携で「突然死しない」🚑
2) Cookie属性の “超ざっくり地図” 🧭🍪
Cookieの事故率が高い属性はこの4つ👇(まずここだけ覚えればOK)
🍪 SameSite(送り先の制限)
Strict:超ガード堅い🛡️(同一サイト以外には基本送らない)Lax:バランス型⚖️(リンク遷移など一部は送る)None:制限しない(=クロスサイトでも送る) ただしSameSite=NoneはSecureも必須(雑に付けると弾かれる)(MDN Web Docs)
🔒 Secure(HTTPSの時だけ送る)
Secure付きCookieは HTTPS通信でしか送られない- 逆に言うと、入口がHTTPSでも、アプリが「HTTPだと思ってる」と事故る(後でやる)😇
🌐 Domain(どのホストに効かせるか)
- 付けない:そのホスト限定(ホスト限定=安全寄り)
Domain=...:サブドメイン含めて広げられる(便利だけど事故りやすい)
“SameSiteの「site」って何?” は、ざっくり 登録可能ドメイン単位(例:
www.web.devはweb.dev)みたいな考え方だよ〜🧠 (web.dev)
🧩 Path(どのURLパスに効かせるか)
Path=/:全部に送るPath=/app1:/app1以下だけに送る パス方式(/app1 /app2)をやると、ここが超重要🔥
3) “地雷あるある” 7連発💣💥(まずここ読むのが最短)
地雷①:SameSite=None なのに Secure 付いてない🍪❌
- ブラウザが「規約違反〜」って感じで弾くことがある
- 対策:
Noneを使うなら 必ずSecure(MDN Web Docs)
地雷②:Secure 付けたのに、入口がHTTPのまま🔒❌
- Cookieは保存されても送られなかったり、そもそも保存されない挙動になる
- 対策:入口をHTTPSに寄せる or Secureを見直す(ローカルHTTPSの章で強くなるやつ💪)
地雷③:Domain を付けて “共有しすぎ” 🌐💥
- 別アプリが同名Cookieを上書き→ログインが飛ぶ🌀
- 対策:基本は Domainを付けずホスト限定にするのが安全
さらに強い守りとして、__Host- プレフィックスが使える(条件:Secure + Path=/ + Domainなし)🛡️
「Domainなしを強制できる」ので事故りにくいよ👍 (IETF Datatracker)
地雷④:Path=/ にして “全部のアプリに送ってる” 🧩💥
- パス方式(/app1 /app2)で特に起きる
- 対策:パス方式なら CookieもPathで分ける(or Cookie名を絶対衝突させない)
地雷⑤:リバプロ越しで “HTTPSだと認識されてない” 😇
- 入口はHTTPSなのに、アプリ側が「HTTPだよね?」と思ってしまう
- 結果:Secure cookieの発行・検証・リダイレクトがズレてログインが死ぬ
- 対策:
X-Forwarded-Proto/X-Forwarded-Hostを信じる設定(アプリ側)を入れる リバプロ側は基本これらを渡すのが普通で、元ホストも伝える設計になってるよ📮 (Caddy Web Server)
地雷⑥:別ドメイン連携(OAuth等)で SameSite が噛み合わない🔁
- “外部から戻ってくる” は サイトをまたぐので、SameSiteが原因でCookieが送られないことがある
- 対策:基本は
Laxを中心に考える(必要時だけ None を検討)(MDN Web Docs)
地雷⑦:ブラウザの「サードパーティCookie制限」影響を食らう🍪🧊
- 近年ブラウザ側の制限が強め
- 特に “埋め込み(iframe)” や “別サイト文脈” は影響を受けやすい
- Chromeは方針が揺れたりもしてるので、「できるだけ同一サイトで完結」させる設計が強い💪 (Reuters)
4) まず最初にやるデバッグ手順🕵️♂️🔍(ここが勝ち筋)
✅ 手順A:レスポンスに Set-Cookie が出てるか?
- ブラウザDevTools → Network
- ログインAPIなどのレスポンスを開く
- Response Headers に
Set-Cookieがあるか確認🍪 (MDN Web Docs)
✅ 手順B:Cookieが “保存” されてるか?
- DevTools → Application → Cookies
- ここに出てなければ「ブラウザに弾かれてる」可能性が高い😇
✅ 手順C:次のリクエストで “送られてる” か?
- Networkで次のAPIリクエストを見て、Request Headers に
Cookie:が付いてるか📨 - 付いてないなら「SameSite / Secure / Domain / Path」が外れてる可能性が高い
5) パターン別 “安全レシピ” 🍳✨
🅰 サブドメイン方式(例:front.localhost / api.localhost)
おすすめ:基本はホスト限定Cookie(Domainなし)
- アプリごとに分離されるので、衝突しにくい🧼
- 「共通ログイン」が必要になった時だけ Domain共有を検討(上級編)
推奨イメージ
SameSite=LaxSecure(入口HTTPS運用なら)HttpOnly(JSから触らせない)Domainは付けない(ホスト限定)
🅱 パス方式(例:example.localhost/app1 と /app2)
おすすめ:Cookie名とPathで分離
app1_sessionはPath=/app1app2_sessionはPath=/app2こうしておくと「別アプリに送られない」ので事故りにくい🧯
6) 実装ミニ例(TypeScript / Node)🧪🍪
例1:安全寄りの “ログインCookie” を発行する(概念)
// 例:ログイン成功時にセッショントークンをCookieへ
const token = "signed-session-token"; // 本物は署名・暗号化などする
res.setHeader("Set-Cookie", [
[
`__Host-session=${encodeURIComponent(token)}`,
"Path=/",
"HttpOnly",
"Secure",
"SameSite=Lax",
"Max-Age=604800", // 7日
].join("; ")
]);
ポイント🍪✨
__Host-を使うと「Domainなし」が前提になって守りが堅い🛡️(条件が満たせないなら無理に使わなくてOK)(IETF Datatracker)SameSite=Laxは “使い勝手と防御のバランス枠” になりやすい (MDN Web Docs)Set-Cookieはヘッダで、複数Cookieは複数行(配列など)で渡すのが基本 (MDN Web Docs)
例2:パス方式で “アプリごとに分離” する
res.setHeader("Set-Cookie", [
"app1_session=aaa; Path=/app1; HttpOnly; Secure; SameSite=Lax; Max-Age=3600",
"app2_session=bbb; Path=/app2; HttpOnly; Secure; SameSite=Lax; Max-Age=3600",
]);
7) リバースプロキシ側で意識すること🚪➡️🏠
✅ “元のHost” と “元のスキーム(http/https)” を上流に伝える
多くのリバプロは X-Forwarded-Host などを付けてくれるけど、上流がHTTPS向け挙動(Secure cookie等)をするには、「プロキシの後ろだよ」設定が必要なことがあるよ〜😇
Caddyの場合も、上流へヘッダを渡しつつ、必要ならHostを上書きする設定がある(HTTPS上流の時など) (Caddy Web Server)
※ここはフレームワーク別に設定名が変わるので、**次の「AIに聞く例」**を使うのが最速👍🤖
8) よくあるミス → 直し方(即効薬)💊✨
-
✅ Cookieが保存されない
SameSite=NoneなのにSecure無し →Secure付ける (MDN Web Docs)SecureなのにHTTPアクセス → 入口HTTPSへ
-
✅ 保存されたのに送られない
Pathがズレてる → 想定URLに合わせるDomainを広げすぎ/間違い → いったんDomain消してホスト限定に戻す
-
✅ 別アプリと干渉してログインが飛ぶ
- Cookie名が衝突 →
app1_session/app2_sessionみたいに分ける Path=/で全部に送ってる → パス方式ならPath=/app1等に分離
- Cookie名が衝突 →
9) ミニ課題🎓🧩(15分で強くなる)
課題1:Cookieの“どこがズレてるか”当てゲーム🔍
- ログインAPIのレスポンスで
Set-Cookieを見る - ApplicationでCookie保存を確認
- 次のAPIで
Cookie:が送られてるか確認 - 送られてなければ SameSite / Secure / Domain / Path のどれが原因かを一言でメモ✍️
課題2:2アプリ共存で “衝突ゼロ” にする🍪🧹
- app1 と app2 を用意して、Cookie名をわざと同じにして衝突させる😈
- 次に Cookie名 + Path で分離して、衝突が消えるのを確認🎉
10) AIに聞く例🤖💬(そのままコピペOK)
🧠 プロキシ配下でSecure cookieが効かない
Node(TypeScript)のWebアプリをリバースプロキシ配下で動かしています。
入口はHTTPS、上流はHTTPです。Secure cookieが発行/送信されません。
使っているフレームワークは(Express/Fastify/Next.js等)です。
必要な “trust proxy” や forwarded headers の設定を最小構成で教えてください。
🍪 パス方式でCookieを安全に分けたい
/app1 と /app2 を同一ドメインで共存させます。
ログインCookieを衝突させたくありません。
Cookie名、Path、SameSite、Secure の推奨セットを
「ローカル開発」と「本番」で分けて提案してください。
🧯 OAuthでログインが戻った瞬間に外れる
OAuthログイン後のリダイレクトでセッションが消えます。
SameSite属性の観点で原因候補と、Lax/Noneの切り替え判断基準を
“CSRF対策”も含めて説明してください。
まとめ🍪✨
- Cookie事故は SameSite / Secure / Domain / Path のどれかが多い
- 複数アプリ同居は「共有しすぎ」が最大の敵:まず ホスト限定 or Path分離🧹
- 入口HTTPS+プロキシ配下は「上流がHTTPSだと理解してない問題」が出るので、ヘッダとアプリ設定がカギ🔑 (Caddy Web Server)
次の第18章(パス方式の設計ミニ練習)では、今日の知識を使って「/app1 /app2 /api でCookie衝突ゼロ設計」を実際に組み立てるよ〜🧩🚀