
CDN のキャッシュを削除しても直らない ── 静的エクスポート構成でフロントエンドだけが古いまま残る問題
リリース直後に「本棚が開けない」という問い合わせが届きました。本棚は購入済み作品の一覧を表示する画面です。リリース前から開いたままのタブで操作を続けたケースでした。ブラウザが読み込んでいるフロントエンドは古く、バックエンドだけが新しい状態です。 CDNのキャッシュはデプロイ時に削除済みです。それでも直りませんでした。 原因はキャッシュが1層ではなかったことです。CDNのパージが届かないキャッシュを、ブラウザ側に2種類残していました。加えて、フロントエンドとバックエンドのバージョンがそろわない状態への備えも足りていませんでした。 先に結論を3点書きます。 CDNのキャッシュ削除が消せるのはエッジまでで、各ユーザーのブラウザHTTPキャッシュには効かない 静的エクスポートしたHTMLにCache-Controlを付けないと、ブラウザが独自の基準でキャッシュする キャッシュ制御とバージョン不整合の検知は、片方だけ実装しても機能しない 前提とする構成 担当しているサービスのフロントエンドは、次の構成で動いています。 Next.js 13.4.7の静的エクスポート(TypeScript、Pages Router) 成果物をオブジェクトストレージ(Alibaba Cloud OSS)に配置し、CDNで配信 APIはGraphQL(graphql-requestと自動生成SDK) バックエンドはフロントエンドと別リポジトリで、デプロイも別 flowchart LR Build["ビルド<br/>next build → 静的エクスポート"] --> OSS["オブジェクトストレージ<br/>Alibaba Cloud OSS"] OSS --> CDN["CDN<br/>エッジキャッシュ"] CDN --> Browser["ブラウザ<br/>HTTPキャッシュ / sessionStorage"] Browser -->|"GraphQL + Client-Version"| API["バックエンド<br/>別リポジトリ・別デプロイ"] 重要なのは、HTMLとJavaScriptがサーバー側でレンダリングされない点です。ブラウザが取得したHTMLと、そこから読み込むJavaScriptチャンクの組み合わせが、そのユーザーにとっての「アプリのバージョン」です。この組み合わせが古いまま固定されると、リリース内容が届きません。 何が起きたか 報告された症状は3つです。 リリース前から開いていたタブで本棚を開くと、画面が壊れる ログアウトして再ログインするまで、リリース内容が反映されない 既存のタブから派生した別のタブで、前のバージョンのデータが表示される 3つには共通点がありました。全ユーザーには起きておらず、CDNのキャッシュを削除しても直りませんでした。そしてログアウトすると直りました。 CDNのキャッシュ削除で直らない事実は、原因の切り分けに使えます。CDNのパージはエッジのキャッシュを消す操作です。エッジを消しても直らないなら、原因はエッジより先、つまりクライアント側にあります。 ブラウザ側にもキャッシュが2種類あった 整理すると、キャッシュはCDNエッジだけではありません。ブラウザ側にも2種類あります。 層 主に保持するもの 消えるタイミング CDNのパージで消えるか CDNエッジ HTML・JavaScript・画像 TTL満了、またはパージ 消える ブラウザHTTPキャッシュ HTML・JavaScript Cache-Controlの指定次第 消えない sessionStorage 画面表示用のデータ タブを閉じる、または明示的な削除 消えない ブラウザHTTPキャッシュ側の問題は2つありました。 1つは、静的エクスポートしたHTMLにCache-Controlが付いていなかったことです。レスポンスに明示的な有効期限がない場合、ブラウザはヒューリスティックキャッシュを行います。つまりLast-Modifiedなどから独自に鮮度を推測して、一定時間は再取得しません。開発者が指定していないだけで、キャッシュしない設定にはなりません。 もう1つは、アップロード方法です。ossutil cp --updateは更新されたファイルを上書きしますが、以前のビルドで生成された古いチャンクを削除しません。結果として、古いHTMLをキャッシュしているブラウザは、そのHTMLが参照する古いJavaScriptチャンクをそのまま読み込み続けます。 ...
