awesome-hacks
Docs

ブラウザのストレージとキャッシュの基礎

Cookie・localStorage・sessionStorage・ブラウザキャッシュの違いと使い分けを整理する

最終更新:2026/06/25

TL;DR

  • Cookie・localStorage・sessionStorage はいずれもブラウザ上にデータを保存する仕組みだが、寿命・容量・HTTPリクエストへの付与が異なる
  • 認証トークンの保存先は「XSSリスクを取るか、CSRF リスクを取るか」の判断になる。どちらも完全に安全ではない
  • ブラウザキャッシュはこの3つとは別物で、HTTPレスポンスそのものを再利用する仕組みであり、サーバー側の Cache-Control ヘッダーで制御する

4つの仕組みの概要

graph LR
    subgraph browser ["ブラウザ"]
        A["Cookie"]
        B["localStorage<br/>(JS からのみ操作)"]
        C["sessionStorage<br/>(JS からのみ操作)"]
        D["ブラウザキャッシュ"]
    end
    subgraph server ["サーバー"]
        E["Webサーバー / API"]
    end

    A -- "HTTPリクエストに自動付与" --> E
    D -- "キャッシュヒット時はリクエスト省略" --> E

Cookie(クッキー)は、サーバーがブラウザに小さなデータを預け、以降のリクエストで自動的に返送させる仕組みである。HTTP ヘッダー(Set-Cookie / Cookie)で送受信する。

たとえばログイン処理では次のように使われる。

sequenceDiagram
    participant B as ブラウザ
    participant S as サーバー

    B->>S: POST /login(メールアドレス・パスワード)
    S-->>B: 200 OK<br/>Set-Cookie: session_id=abc123 HttpOnly Secure

    Note over B: Cookie を保存

    B->>S: GET /mypage<br/>Cookie: session_id=abc123
    S-->>B: 200 OK(ログイン済みユーザーのページ)

ブラウザは Set-Cookie で受け取った値を保存し、同じドメインへの次のリクエストに Cookie ヘッダーとして自動で付与する。サーバーはその値を見て「このリクエストは誰のものか」を判断できる。

特徴

  • 寿命Expires または Max-Age で指定する。省略するとセッションが終わる(ブラウザを閉じる)まで
  • 容量:4 KB 程度(ドメインごと)
  • HTTP リクエストへの付与:同一ドメインへのリクエストに自動で付与される
  • JS からのアクセスdocument.cookie で読み書きできるが、HttpOnly フラグを付けると JS からアクセス不可になる
Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=Strict; Max-Age=3600
フラグ意味
HttpOnlyJS から読み取り不可。XSS でトークンを盗まれるリスクを下げる
SecureHTTPS 通信のときのみ送信する
SameSite=Strict別サイトからのリクエスト(フォーム送信など)には Cookie を付けない。CSRF 対策になる
SameSite=Lax別サイトからのナビゲーション(リンクのクリック)では付けるが、フォーム POST には付けない
SameSite=Noneクロスサイトのリクエストにも付ける(Secure 必須)
Max-Age=NN 秒後に削除する

localStorage

localStorageは、ブラウザ上に半永久的にデータを保存できるキー・バリュー型の API である。

特徴

  • 寿命:明示的に削除するまで残る(ブラウザを閉じても消えない)
  • 容量:5 MB 程度(ブラウザ・オリジンごと)
  • HTTP リクエストへの付与:自動では付与されない。JS で明示的に取り出してヘッダーに入れる必要がある
  • JS からのアクセスlocalStorage.setItem / getItem / removeItem で操作する
  • スコープ:オリジン(スキーム + ドメイン + ポート)単位。同一オリジン内のすべてのタブで共有される
localStorage.setItem("theme", "dark");
const theme = localStorage.getItem("theme"); // "dark"
localStorage.removeItem("theme");

sessionStorage

sessionStorageは localStorage と同じキー・バリュー型の API だが、タブ(セッション)ごとに独立している点が異なる。

特徴

  • 寿命:タブを閉じると削除される
  • 容量:5 MB 程度
  • HTTP リクエストへの付与:しない
  • JS からのアクセスsessionStorage.setItem / getItem / removeItem(localStorage と同じ API)
  • スコープ:タブ単位。同じオリジンでも別タブとはデータを共有しない
sessionStorage.setItem("wizard_step", "2");
const step = sessionStorage.getItem("wizard_step"); // "2"

localStorage と sessionStorage の使い分け

API は同一なので、どちらを使うかは「データをどのスコープで生かすか」だけで決まる。

判断の起点は「タブをまたぐか/閉じても残すか」

graph TD
    Start["データを保存したい"]
    Start --> Q1{"タブを閉じたら<br/>リセットしてよいか?"}
    Q1 -- "残したい" --> Q2{"複数タブで<br/>同じ値を共有したいか?"}
    Q1 -- "消えてよい" --> Q3{"複数タブで<br/>状態が混ざっては困るか?"}

    Q2 -- "共有したい" --> LS["localStorage"]
    Q2 -- "タブ別でよい" --> Q3
    Q3 -- "困る" --> SS["sessionStorage"]
    Q3 -- "混ざっても問題ない" --> SS2["sessionStorage<br/>(または localStorage でも可)"]

具体的なユースケース

localStorage を選ぶケース

UI 設定(テーマ・言語・フォントサイズ)
ユーザーが「ダークモード」に切り替えたら、次回アクセス時も維持したい。別タブでも同じ設定を使いたい。

// ダークモード切り替え
localStorage.setItem("theme", "dark");

// 次回アクセス時に復元
const theme = localStorage.getItem("theme") ?? "light";
document.documentElement.setAttribute("data-theme", theme);

ゲストユーザーのカート
ログインしていないユーザーが商品をカートに入れた状態を、ブラウザを閉じても保持したい。別タブで同じカートを参照・編集できるようにしたい。

const cart = JSON.parse(localStorage.getItem("cart") ?? "[]");
cart.push({ id: 42, name: "商品A", qty: 1 });
localStorage.setItem("cart", JSON.stringify(cart));

sessionStorage を選ぶケース

多ステップフォーム(申し込みウィザード)
「Step 1 → Step 2 → Step 3 → 確認 → 送信」のように複数ページにまたがるフォームの途中入力を保持したい。タブを閉じたらリセットしてほしい。また、同じ申し込みフォームを別タブで開いたときに入力内容が混ざっては困る。

// Step 1 完了時に保存
sessionStorage.setItem("apply_step1", JSON.stringify({ name: "山田太郎", email: "..." }));

// Step 2 でStep 1 の値を参照
const step1 = JSON.parse(sessionStorage.getItem("apply_step1") ?? "{}");

ページ内の一時的なフィルター・ソート状態
「カテゴリ:電化製品、並び順:価格昇順」のような検索条件を、ページ遷移をまたいで保持したい。ただしブラウザを閉じたらデフォルトに戻ってほしい。別タブで別の検索をしている場合は互いに干渉させたくない。

sessionStorage.setItem("filter", JSON.stringify({ category: "electronics", sort: "price_asc" }));

まとめ

localStoragesessionStorage
次回アクセスも残す✗(タブを閉じると消える)
別タブで共有✗(タブごとに独立)
タブを閉じてリセット✗(明示削除が必要)
タブごとに独立した状態
向いている用途設定・カート・長期保持データウィザード・一時フィルター・タブ固有状態

ブラウザキャッシュ

ブラウザキャッシュは、一度取得した HTTP レスポンス(HTML・JS・画像など)を再利用する仕組みである。上記 3 つとは性質が異なり、JS から直接操作するものではなく、サーバーの Cache-Control ヘッダーによって制御される。

詳細な仕組みは Cache-Control ヘッダーの基礎 に記載している。

概要

sequenceDiagram
    participant B as ブラウザ
    participant C as ブラウザキャッシュ
    participant S as サーバー

    B->>S: GET /app.js
    S-->>B: 200 OK / Cache-Control: max-age=31536000
    Note over C: レスポンスを保存

    B->>C: GET /app.js(2回目)
    C-->>B: キャッシュから即返却(サーバーへのリクエストなし)

Cookie・localStorage との違い

ブラウザキャッシュが保存するのは「HTTP レスポンス」全体(ヘッダーを含む)であり、アプリのデータを保存するものではない。用途がまったく異なる。


比較まとめ

CookielocalStoragesessionStorageブラウザキャッシュ
寿命Expires / Max-Age で設定明示削除までタブを閉じるまでCache-Control で設定
容量約 4 KB約 5 MB約 5 MBブラウザ設定次第(数百 MB〜)
HTTP 自動送信ありなしなし—(リクエスト自体をスキップ)
JS アクセス可(HttpOnly で禁止可)不可
スコープドメイン + パスオリジンタブ + オリジンオリジン + URL
操作主体サーバー / JSJSJSサーバー(ヘッダーで制御)
主な用途セッション、認証トークンテーマ、ユーザー設定、永続トークン一時的なフォーム状態、ウィザード進捗静的ファイル、API レスポンスの再利用

使い分けの指針

認証トークンをどこに保存するか

認証トークン(セッション ID・JWT など)の保存先は、XSSCSRF の二つのリスクのトレードオフになる。

graph TD
    A["認証トークンの保存先を選ぶ"]
    A --> B["Cookie(HttpOnly + SameSite)"]
    A --> C["localStorage / sessionStorage"]

    B --> D["XSS でトークンを盗まれにくい<br/>(JSから読めない)"]
    B --> E["CSRF 攻撃のリスクがある<br/>(SameSite=Strict で対策可)"]

    C --> F["CSRF リスクはない<br/>(自動送信されないため)"]
    C --> G["XSS 攻撃でトークンを盗まれるリスクがある"]

一般的には Cookie(HttpOnly + Secure + SameSite=Strict)が推奨される。XSS の防止が難しい環境では、それでも JS でトークンを読む必要がないため被害を限定できる。

ケース別の選択

ケース推奨
セッション ID・認証トークンCookie(HttpOnly + Secure + SameSite)
UI 設定(テーマ・言語など)・永続的なユーザー設定localStorage
複数タブで共有してほしくない一時データ(フォームの途中入力、ウィザードの状態)sessionStorage
静的ファイル(JS・CSS・画像)の配信最適化ブラウザキャッシュ(Cache-Control)
API レスポンスの短期キャッシュブラウザキャッシュ(Cache-Control)

よくある落とし穴

HttpOnly なしの Cookie は document.cookie で JS から読める。XSS が成立するとトークンを盗まれる。認証 Cookie には必ず HttpOnly を付ける。

SameSite 未設定(または None)の Cookie は、他サイトからのリクエストにも付与される。これが CSRF の起点になる。現代のブラウザは未設定の場合 Lax として扱うことが多いが、明示的に指定するのが望ましい。

localStorage にトークンを入れて「安全」と思い込む

CSRF を回避できるが XSS には無防備である。XSS を防げていなければ、localStorage の内容はすべて漏洩する。どちらを使う場合も、XSS 対策(CSP ヘッダー、入力サニタイズ、信頼できないスクリプトの排除)は必須。

sessionStorage のスコープはタブ単位である。別タブで同じサイトを開いてもデータは共有されない。「ブラウザを閉じたら消える」点はセッション Cookie と同様だが、「タブを閉じただけで消える」点が異なる。


関連