• 診断項目 No.31/127
  • Cookie・セッション管理
  • 重大度 重要(high)

機密ページのキャッシュ制御とは?Cache-Control: no-storeの必要性・診断基準・設定方法

機密ページのキャッシュ制御は、ログイン画面などの応答にCache-Control: no-storeを付け、ブラウザや共有キャッシュに保存させない対策です。no-cache・privateとの違い、bfcacheの注意点、Apache・nginx等の設定例を示します。

結論(機密ページのキャッシュ制御)

機密ページのキャッシュ制御とは、ログイン画面・管理画面・マイページなど個人や業務の情報を表示するページの応答に「Cache-Control: no-store」を付け、ブラウザやプロキシ、CDNに保存させないようにすることです。no-cacheやprivateは保存自体を防がないため不十分です。セキュリティAIチェッカーは、入力URLがログイン・管理・アカウント系のページで2xx応答のとき、no-storeがあれば ○、なければ × と判定します。

この記事のポイント

  • no-store だけが「どのキャッシュにも保存しない」指示で、no-cache・private・max-age=0 は保存を防ぎません。
  • HTTPSの応答もブラウザやCDNにキャッシュされ、HTMLのmetaタグやPragmaでは代わりになりません。
  • 診断は最終URLのパスが admin・login・signin・sign-in・account・mypage・my-page の機密ページで2xxのときだけ評価し、それ以外は対象外です。
  • no-store を含めば ○、含まなければ ×(重み3が得点0)。トップページの通常診断では対象外になります。
  • Chromeは no-store のページもbfcacheに保存するようになったため、ログアウト時のセッションCookie削除が前提になります。
  • no-store はサイト全体ではなく、ログイン・管理系のパスだけに付けます。
診断項目名機密ページのキャッシュ制御
項目IDheaders.cache
分類Cookie・セッション管理
重大度・配点の重み重要(high) 重み 3
関連する標準・ガイドラインOWASP ASVS 5.0.0 V14.3.2(機密データがブラウザにキャッシュされないよう Cache-Control: no-store 等の十分なキャッシュ抑止ヘッダーを設定、L2)・V14.2.2(ロードバランサー等のサーバー側キャッシュに機密データを残さない、L2)、OWASP WSTG-ATHN-06(Browser Cache Weaknesses)、RFC 9111(HTTP Caching)5.2.2.5 no-store、CWE-525(機密情報を含むWebブラウザキャッシュの使用)

機密ページのキャッシュ制御とは(Cache-Control: no-store)

機密ページのキャッシュ制御とは、ログイン画面、管理画面、マイページのように個人や業務の情報を表示するページの応答に Cache-Control: no-store を付け、ブラウザやプロキシ、CDNに保存させないようにすることです。セキュリティAIチェッカーはこの項目(headers.cache)で、診断対象として入力されたURLがログイン・管理・アカウント系のページである場合に、応答に Cache-Control: no-store が付いているかを確認します。

ブラウザやプロキシ、CDNは、表示を速くするためにWebページの応答を保存(キャッシュ)します。公開情報なら問題ありませんが、機密ページがキャッシュされると、共用端末の後の利用者や、経路上のキャッシュサーバーを通じて第三者に表示されるおそれがあります。

項目内容
項目IDheaders.cache
診断カテゴリCookie・キャッシュ
重大度high
重み3
関連標準OWASP ASVS 5.0.0 V14.3.2・V14.2.2(L2)、OWASP WSTG-ATHN-06、RFC 9111 5.2.2.5 no-store、CWE-525
関連項目「認証関連ページのno-store」:巡回で見つかった認証関連ページのno-store

no-store・no-cache・privateの違い(Cache-Controlの主な指示子)

指示子意味(MDN・RFC 9111)機密ページでの評価
no-store共有・私的を問わず、どのキャッシュにも応答を保存しない推奨
no-cache保存は可能だが、再利用の前に必ずオリジンサーバーで再検証する不十分(ディスクに保存され得る)
private共有キャッシュ(プロキシ、CDN)には保存せず、ブラウザ等の私的キャッシュのみ保存可不十分(ブラウザに残る)
max-age=0 / must-revalidate期限切れ扱い・再検証の要求不十分

MDNは「no-cache は『キャッシュするな』という意味ではない」と注意し、応答を保存させたくない場合は no-store を使うよう明記しています。WSTG-ATHN-06も、機密情報を含むページでは Cache-Control: no-store を設定すること、no-store を使えば must-revalidate、max-age、Expires などの追加指示は現代のブラウザでは一般に不要であることを示しています。

キャッシュ制御についてよくある誤解

  • 「HTTPSならキャッシュされない」:HTTPSの応答もブラウザやCDN(TLS終端後)にキャッシュされます。
  • 「Pragma: no-cache で十分」:Pragma はHTTP/1.0時代の要求ヘッダー向けの仕組みで、応答での効果は保証されません。Cache-Control を使ってください。
  • 「HTMLの <meta http-equiv="Cache-Control"> で指定できる」:metaタグはHTTPキャッシュの判定に使われません。必ずHTTP応答ヘッダーで指定します。

「戻る」ボタンとbfcache:no-storeだけでは足りない場面

ブラウザには、直前のページを丸ごとメモリに保持して「戻る」を瞬時にする bfcache(Back/Forward Cache)があります。MDNは、no-cache は「戻る」等の履歴ナビゲーションでの再検証を保証しないと注意しています。

従来、ページ本体に Cache-Control: no-store が付いている場合、ブラウザはそのページをbfcacheに保存しませんでした。しかしChromeは、2025年3〜4月に全利用者への展開を完了した変更により、no-store のページもbfcacheへ保存するようになりました。ただし、Cookie等の認証状態が変化した場合はbfcacheから破棄され、保持時間も3分に短縮されます。

したがって、ログアウト時にセッションCookieを確実に削除することが、「戻る」で個人情報を再表示させないための前提になります。web.devは、pageshow イベントで認証用Cookieの有無を確認し、なければ再読み込みする実装も紹介しています。HTTPキャッシュ(ディスク・共有キャッシュ)への保存を防ぐ基本対策は、引き続き no-store です。

公開ページにno-storeは不要

no-storeを全ページに付けると、表示速度の低下、サーバー負荷の増加、bfcacheが効かないことによる体験の悪化を招きます。web.devも、no-store は機密情報を含むページに限定し、常に最新を表示したいだけのページには no-cache や max-age=0 を使うよう推奨しています。公開ページが対象外(na)になっているのはこのためです。

機密ページのキャッシュ制御の診断基準(セキュリティAIチェッカー)

取得対象

  • 入力URLへのGET(リダイレクト最大4回追跡)で得た最終URLのパスと、その応答の Cache-Control ヘッダー。
  • ログインページへリダイレクトされる管理画面URLを入力した場合は、リダイレクト後のログインページが評価対象になります。

機密ページとみなす条件

最終URLのパスに、次のいずれかの区切られた語が含まれる場合(大文字小文字を区別しない)。

  • 対象語:admin、login、signin、sign-in、account、mypage、my-page
  • 語の直前は /。語の直後は、/、パスの末尾、または「.php .html .htm .asp .aspx .jsp .cgi の拡張子でパスが終わる」のいずれか
  • クエリ文字列は判定に使いません
パスの例機密ページとみなすか
/login、/admin/、/city/mypage/index.html、/login.php対象
/administration-guide/、/news/login-campaign、/accounts/、/login.do対象外

判定条件

判定条件得点への影響
○ 問題なし(pass)2xx応答の機密ページで、Cache-Control の値に no-store という文字列を含む(大文字小文字を区別しない)重み3を満点で加算
× 要改善(fail)2xx応答の機密ページで、Cache-Control がない、または no-store を含まない(no-cache、private のみ等)重み3が分母に入り得点0
対象外(na)最終URLが上記の機密ページ条件に一致しない(公開ページ)、または一致しても応答が2xxでない(404・500など。実際のページ内容が返っていないため)分母から除外
  • 根拠欄には観測した Cache-Control の値(ない場合は「Cache-Controlがありません。」)を表示します。公開ページの対象外では「公開ページのため、この項目は対象外です。」と表示します。
  • 入力URLの応答が403または429の場合は、アクセスが制限されたものとして診断全体がエラーになり、レポートは生成されません。
  • トップページ(/)を入力した通常の診断では対象外になります。巡回で見つかったログイン・管理系ページは、関連項目「認証関連ページのno-store」が同じパス条件・同じ2xx条件で評価します。

自治体サイトでの判定例

診断したURLと応答判定
https://www.city.example.lg.jp/対象外
電子申請やイベント予約システムのログイン画面 https://yoyaku.city.example.lg.jp/login で Cache-Control: no-store, no-cache, must-revalidate○
同上で Cache-Control: private, max-age=0×
CMSの管理画面 /admin/ がCDN経由で Cache-Control: max-age=600×(共有キャッシュに保存されるおそれが高い)

機密ページがキャッシュされるとどんなリスクがあるか

  • 共用端末での情報露出:図書館や窓口の端末で、前の利用者のマイページや申請内容が「戻る」操作やキャッシュから表示される。WSTG-ATHN-06は、ログアウト後に「戻る」で機密データが表示されるケースを検査対象としています。
  • CDN・プロキシでの取り違え:ログイン後のページが共有キャッシュに保存されると、別の利用者に他人の個人情報ページが配信される重大事故につながります。
  • 端末内への残存:ブラウザのディスクキャッシュに個人情報を含むHTMLが残り、端末の紛失・盗難やマルウェア感染時に読み取られます。

機密ページのキャッシュ制御の設定方法(Apache・PHP・nginx・IIS・Next.js・CDN)

Apache(.htaccess):ログイン・管理系だけに付与

<IfModule mod_headers.c>
  <If "%{REQUEST_URI} =~ m#/(admin|login|signin|sign-in|account|mypage|my-page)(/|\.php|\.html?|$)#i">
    Header always set Cache-Control "no-store"
  </If>
</IfModule>

<If> はApache 2.4以降で使えます。管理画面ディレクトリが分かれているなら、そのディレクトリの .htaccess に Header always set Cache-Control "no-store" を置くのが簡単です。

PHP(アプリケーション側)

<?php
// ログイン画面・マイページの先頭(出力前)
header('Cache-Control: no-store');

PHPのセッションを使う場合、session_cache_limiter('nocache')(既定値)で送られるヘッダーは no-store, no-cache, must-revalidate を含みます。設定で private 等に変更していないか確認してください。

nginx

location ~* ^/(admin|login|mypage)(/|$) {
    proxy_hide_header Cache-Control;   # 上流の値と二重に出力しない
    add_header Cache-Control "no-store" always;
    proxy_pass http://app_backend;
}

この例の正規表現は主要なパスだけを挙げています。signin・account や /login.php など、実際のログイン・管理系URLに合わせて追加してください。また、location 内に add_header を書くと server レベルの add_header(他のセキュリティヘッダーを含む)は継承されないため、必要なヘッダーは同じ location にも記述してください。

IIS(web.config、管理ディレクトリ配下)

<configuration>
  <system.webServer>
    <httpProtocol>
      <customHeaders>
        <remove name="Cache-Control" />
        <add name="Cache-Control" value="no-store" />
      </customHeaders>
    </httpProtocol>
  </system.webServer>
</configuration>

Next.js(Vercel)

// next.config.js
module.exports = {
  async headers() {
    return [
      { source: '/(admin|login|mypage)/:path*', headers: [{ key: 'Cache-Control', value: 'no-store' }] },
    ];
  },
};

App Routerで認証情報を使うページは動的レンダリングにし、cookies() 等を参照するとキャッシュされない応答になります。最終的な応答ヘッダーで必ず確認してください。

CDNの設定

CDN(CloudFront、Cloudflare等)では、ログイン・管理系パスを「キャッシュしない」ルールにし、オリジンの no-store を尊重する設定にします。

確認方法(curl)

curl -sI https://yoyaku.city.example.lg.jp/login | grep -i '^cache-control'
# 期待値: Cache-Control: no-store(他の指示子が併記されていても可)

設定後は、ログイン画面のURLを入力してセキュリティAIチェッカーで再診断し、本項目が ○ になったことを確認します。

サイトを壊さないための注意点(ガードレール)

  • サイト全体に no-store を付けないでください。公開ページの表示速度とbfcacheが損なわれ、CDNの効果もなくなります。
  • Header set と Header always set が重複すると、エラー応答とそれ以外でヘッダーが二重になることがあります。設定後は通常応答とリダイレクト応答の両方を確認してください。

よくある質問(FAQ)

Cache-Controlの no-store と no-cache の違いは何ですか?

no-store は共有・私的を問わずどのキャッシュにも応答を保存させない指示です。no-cache は保存は可能で、再利用の前にオリジンサーバーで再検証させる指示にすぎず、ディスクに保存され得ます。応答を保存させたくない場合は no-store を使います。

Cache-Control: private ならログイン画面に十分ですか?

不十分です。private はプロキシやCDNなどの共有キャッシュには保存させませんが、ブラウザの私的キャッシュには保存を許すため、共用端末や端末の紛失時に情報が残ります。

トップページを診断したら「対象外」になりました。なぜですか?

この項目は、入力URLの最終パスがログイン・管理・アカウント系のページのときだけ評価します。公開ページにno-storeは不要なため、トップページの通常診断では対象外です。巡回で見つかったログイン・管理系ページは「認証関連ページのno-store」の項目で評価します。

全ページに no-store を付けたほうが安全ではありませんか?

おすすめしません。表示速度の低下、サーバー負荷の増加、bfcacheが効かないことによる体験の悪化を招き、CDNの効果もなくなります。no-store は機密情報を含むページに限定し、常に最新を表示したいだけのページには no-cache や max-age=0 を使います。

no-store を付ければ「戻る」ボタンで個人情報が表示されることはありませんか?

それだけでは保証されません。Chromeは2025年に no-store のページもbfcacheに保存する変更を全利用者へ展開しました。Cookie等の認証状態が変わればbfcacheから破棄されるため、ログアウト時にセッションCookieを確実に削除することが前提になります。

HTMLのmetaタグでCache-Controlを指定できますか?

できません。metaタグはHTTPキャッシュの判定に使われません。Pragma: no-cache も応答での効果は保証されないため、必ずHTTP応答ヘッダーの Cache-Control で指定します。

PHPのセッションを使っている場合は設定が必要ですか?

session_cache_limiter('nocache')(既定値)で送られるヘッダーは no-store, no-cache, must-revalidate を含みます。設定で private 等に変更していないかを確認してください。

セキュリティAIチェッカーを利用する

この項目を含む127項目を、URLを入れるだけで無料診断します。

参考資料