20260623tue
開発環境の localhost からプロキシで API にリクエストしてもキャッシュとかは発行されるの?
プロキシ経由のクッキー発行メカニズムを解析した。
「キャッシュ」というのが具体的に何を指しているかで答えが変わってきますが、たぶん「ログイン時に発行されるトークン(cookie)はちゃんと発行されるのか?」という意味だと思うので、その前提で答えます(違っていたら教えてください)。
結論:プロキシ経由でも基本的には正常に発行されます
開発環境でよくやる構成は、nuxt.config.js の proxy 設定(@nuxtjs/proxy や http-proxy-middleware)で http://localhost:3000/api/* → https://本番APIサーバー/api/* のように転送する形です。
これをやる理由は CORS 回避です。ブラウザから直接本番 API に fetch すると、オリジン(ドメイン)が違うのでブラウザが CORS エラーで止めることがあります。プロキシを使うと、ブラウザからは「localhost に対してリクエストしている」ように見えるので、CORS の制約に引っかかりません。
Set-Cookie もプロキシ越しなら基本通る
サーバーが Set-Cookie ヘッダーを返せば、ブラウザは「自分(=localhost)からのレスポンス」として受け取るので、cookie は正常に保存されます。プロキシサーバー(Nuxt の dev サーバー)がレスポンスヘッダーをそのまま転送してくれるからです。
ただし、ここで詰まりやすい落とし穴がいくつかあります
cookie の domain 属性
本番 API が Set-Cookie: token=xxx; Domain=本番ドメイン のように明示的に domain を指定していると、ブラウザは「localhost からのレスポンスなのに domain が違う」と判断して cookie を保存してくれません。この場合は開発環境用に domain 指定を外したレスポンスを返すよう、バックエンド側で環境分岐するか、プロキシ側でレスポンスヘッダーを書き換える必要があります。
Secure 属性と http/https
Set-Cookie に Secure 属性が付いていると、HTTPS 環境でしか cookie が保存されません。開発環境が http://localhost の場合、Secure 付きの cookie は保存されないので、開発環境用に Secure を外す設定が必要になることが多いです。
SameSite 属性
SameSite=Strict や None の設定によっても、プロキシ構成だと挙動が変わることがあります。特に SameSite=None の場合は Secure も同時に必須なので、上記と絡んで開発環境でつまずきやすいポイントです。
確認する方法
実際に挙動を見るには、ブラウザの DevTools → Network タブで Set-Cookie ヘッダーが返ってきているか、Application タブ → Cookies で実際に保存されているかを見るのが一番確実です。「リクエストは通っているが cookie が保存されない」場合は上記の domain/Secure/SameSite のどれかが原因のことが多いです。
@nuxtjs/auth の strategy 設定で local(独自 API+JWT 想定)になっているか確認すれば、今回が JWT 方式かどうか確定できます。
4つの言葉の整理
4つの言葉は実は「何を」「どこに」保存するかという別々の軸の話なので、整理します。
① cookie=「保存場所」の一つ
ブラウザが持つ保存領域の種類。localStorage や Vuex のメモリ上の状態と同じ「容れ物」の選択肢の一つです。中に何を入れるかは関係なく、ただの箱。
② キャッシュ=「サーバーに聞き直さず使い回す」という考え方全般
厳密な用語ではなく、「一度取得した情報を保持して再利用する」という広い概念。cookie に入れても Vuex に入れても、サーバーに聞き直さず使い回していれば「キャッシュしている」と言えます。
③ JWT とセッション=「どこに状態を持たせるか」の 2 つの方式
ここが一番の核心です。
| JWT 方式 | セッション方式 | |
|---|---|---|
| 状態を持つのは | ブラウザ側(トークン自体に情報が入ってる) | サーバー側(DB やメモリにユーザー情報を保持) |
| ブラウザが持つのは | ユーザー情報入りのトークン本体 | 意味のないランダムな ID(セッション ID)だけ |
| サーバーの負荷 | リクエストごとに DB を見なくていい(ステートレス) | リクエストごとにセッション ID から DB を引く必要がある |
| 失効させたい時 | トークンの有効期限切れを待つしかない(即時失効が難しい) | サーバー側でセッションを削除すれば即時失効できる |
④ どこに保存するかは方式と無関係に選べる
ここが混乱しやすいポイントです。JWT 方式でもセッション方式でも、ブラウザ側の保存先は cookie でも localStorage でもどちらも使えます。「JWT=cookie」「セッション=cookie」みたいな固定の組み合わせはなく、保存場所(cookie)と状態管理方式(JWT/セッション)は独立した話です。
まとめ
- JWT vs セッション=「状態をブラウザに持たせるか、サーバーに持たせるか」という設計方式の話
- cookie=ブラウザ側でその状態(トークンやセッション ID)を「どこに置くか」という保存場所の話
- キャッシュ=そもそも「サーバーに聞き直さず保持して使い回す」という行為全般を指す、ふわっとした言葉
今回の構成(@nuxtjs/auth がトークン+ロールを受け取って cookie/Vuex で保持)は、JWT 方式を、cookie という保存場所に置いて、キャッシュとして使い回しているという状態です。
Set-Cookie の仕組み
サーバーがレスポンスのヘッダーに Set-Cookie: token=eyJhbGc...; 各種属性 という一行を入れて返します。これはデータそのものではなく、「これを cookie という場所に保存しておいてね」という指示です。ブラウザはこのヘッダーを見て、自分の中の「cookie 保存領域」(=容れ物)にその値を書き込みます。
- 「容れ物」自体はブラウザ側に元から存在している(ブラウザの機能として)
- サーバーが返しているのは「容れ物に入れる中身+保存条件の指示書」(Set-Cookie ヘッダー)
箱の比喩で言うと
サーバーは箱(容れ物)を渡しているわけではなく、「この荷物(トークン)を、あなたの家にある”cookie 箱”に、こういう条件(有効期限・どのページでだけ使うか等)で保管しておいてください」という配送指示書付きの荷物を渡している、というイメージです。ブラウザ自身が「cookie 箱」というハードウェア(保存機構)を最初から持っていて、サーバーはその使い方を指示しているだけ、というのが一番正確な理解です。
ライブラリ選択の順番
正確に言うと:
- まず「認証」「認可」「JWT/セッション」「cookie」のような概念・方式を理解する
- プロジェクトの要件(SSR/SSG/CSR 構成、即時失効が必要か、マイクロサービス間で使うか等)に応じてどの方式を採用するかを決める
- その方式を実装するためにライブラリを選ぶ(
@nuxtjs/auth、@sidebase/nuxt-auth、Auth0、Firebase Auth など)
順番が大事で、ライブラリ先に選んで後から概念を当てはめるのではなく、概念 → 方式 → ライブラリ の順で考えるのが正しいアプローチです。ライブラリはあくまで「選んだ方式を楽に実装するための道具」であって、ライブラリが変わっても認証・認可という概念自体は変わりません。
実際、@nuxtjs/auth が Nuxt3 で @sidebase/nuxt-auth に変わったのも、ツールが変わっただけで「JWT を cookie に保存して middleware で認可判定する」という概念レベルの設計は同じです。なのでツールの使い方を覚える前に、今回整理した概念をちゃんと理解しておけば、ライブラリが変わっても応用が効きます。