Skip to content

20260623tue

Published: at 15:00

20260623tue

開発環境の localhost からプロキシで API にリクエストしてもキャッシュとかは発行されるの?

プロキシ経由のクッキー発行メカニズムを解析した。

「キャッシュ」というのが具体的に何を指しているかで答えが変わってきますが、たぶん「ログイン時に発行されるトークン(cookie)はちゃんと発行されるのか?」という意味だと思うので、その前提で答えます(違っていたら教えてください)。


結論:プロキシ経由でも基本的には正常に発行されます

開発環境でよくやる構成は、nuxt.config.js の proxy 設定(@nuxtjs/proxyhttp-proxy-middleware)で http://localhost:3000/api/*https://本番APIサーバー/api/* のように転送する形です。

これをやる理由は CORS 回避です。ブラウザから直接本番 API に fetch すると、オリジン(ドメイン)が違うのでブラウザが CORS エラーで止めることがあります。プロキシを使うと、ブラウザからは「localhost に対してリクエストしている」ように見えるので、CORS の制約に引っかかりません。

サーバーが Set-Cookie ヘッダーを返せば、ブラウザは「自分(=localhost)からのレスポンス」として受け取るので、cookie は正常に保存されます。プロキシサーバー(Nuxt の dev サーバー)がレスポンスヘッダーをそのまま転送してくれるからです。

ただし、ここで詰まりやすい落とし穴がいくつかあります

cookie の domain 属性

本番 API が Set-Cookie: token=xxx; Domain=本番ドメイン のように明示的に domain を指定していると、ブラウザは「localhost からのレスポンスなのに domain が違う」と判断して cookie を保存してくれません。この場合は開発環境用に domain 指定を外したレスポンスを返すよう、バックエンド側で環境分岐するか、プロキシ側でレスポンスヘッダーを書き換える必要があります。

Secure 属性と http/https

Set-CookieSecure 属性が付いていると、HTTPS 環境でしか cookie が保存されません。開発環境が http://localhost の場合、Secure 付きの cookie は保存されないので、開発環境用に Secure を外す設定が必要になることが多いです。

SameSite 属性

SameSite=StrictNone の設定によっても、プロキシ構成だと挙動が変わることがあります。特に 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/セッション)は独立した話です。


まとめ

今回の構成(@nuxtjs/auth がトークン+ロールを受け取って cookie/Vuex で保持)は、JWT 方式を、cookie という保存場所に置いて、キャッシュとして使い回しているという状態です。


サーバーがレスポンスのヘッダーに Set-Cookie: token=eyJhbGc...; 各種属性 という一行を入れて返します。これはデータそのものではなく、「これを cookie という場所に保存しておいてね」という指示です。ブラウザはこのヘッダーを見て、自分の中の「cookie 保存領域」(=容れ物)にその値を書き込みます。

箱の比喩で言うと

サーバーは箱(容れ物)を渡しているわけではなく、「この荷物(トークン)を、あなたの家にある”cookie 箱”に、こういう条件(有効期限・どのページでだけ使うか等)で保管しておいてください」という配送指示書付きの荷物を渡している、というイメージです。ブラウザ自身が「cookie 箱」というハードウェア(保存機構)を最初から持っていて、サーバーはその使い方を指示しているだけ、というのが一番正確な理解です。


ライブラリ選択の順番

正確に言うと:

  1. まず「認証」「認可」「JWT/セッション」「cookie」のような概念・方式を理解する
  2. プロジェクトの要件(SSR/SSG/CSR 構成、即時失効が必要か、マイクロサービス間で使うか等)に応じてどの方式を採用するかを決める
  3. その方式を実装するためにライブラリを選ぶ(@nuxtjs/auth@sidebase/nuxt-auth、Auth0、Firebase Auth など)

順番が大事で、ライブラリ先に選んで後から概念を当てはめるのではなく、概念 → 方式 → ライブラリ の順で考えるのが正しいアプローチです。ライブラリはあくまで「選んだ方式を楽に実装するための道具」であって、ライブラリが変わっても認証・認可という概念自体は変わりません。

実際、@nuxtjs/auth が Nuxt3 で @sidebase/nuxt-auth に変わったのも、ツールが変わっただけで「JWT を cookie に保存して middleware で認可判定する」という概念レベルの設計は同じです。なのでツールの使い方を覚える前に、今回整理した概念をちゃんと理解しておけば、ライブラリが変わっても応用が効きます。