マガジン

キャッシュはゲームの敵か味方か:更新が反映されない理由

遊びを見つける

執筆にClaudeを使用しています。

試作の段ボールから入口やゴールを備えた完成形へつながるゲーム模型の写真風イメージ。
記事のテーマをもとに制作したAI生成イラスト。

ゲームの不具合を直して公開内容を更新したのに、自分の画面ではまだ直っていない。そんな場面を考えてみます。ファイルは確かに差し替えたはずで、コードにも間違いはありません。それでも画面には古い挙動が残っています。多くの場合、原因はゲームのコードではなく、ブラウザが「前に受け取ったファイル」をそのまま使っていることにあります。この仕組みがキャッシュ(一度受け取ったデータを手元に残し、次回の読み込みに使い回すこと)です。 結論を先に言えば、キャッシュは敵でも味方でもなく、使い方で変わる道具です。再訪問のたびに同じ画像や音を取り直さずに済むのは、キャッシュのおかげです。一方で、同じ名前のファイルが中身だけ入れ替わると、ブラウザはそれに気づけません。この記事では、古い版が表示される仕組みを層に分けて整理します。そのうえで、ファイル名で版を示す「バージョン付きURL」の考え方と、自分の作品で切り分けるための実験の設計書を示します。

この記事の目次
  1. 同じURLなら、ブラウザは「前に見たもの」を使う
  2. 古い版が出る場所は一つではない
  3. バージョン付きURLという考え方
  4. アソバースの仕様から読み取れること
  5. 自分で切り分ける実験の設計書
  6. 次の行動
  7. 参考資料

同じURLなら、ブラウザは「前に見たもの」を使う

ブラウザは、読み込んだファイルをURLという「住所」で覚えています。同じ住所の依頼が来たとき、手元のコピーがまだ使えると判断すれば、通信せずにそれを使います。使えるかどうかは、サーバーが返す指示(Cache-Controlなどのヘッダー)や、ブラウザ自身の判断で決まります。

ここに更新が反映されない理由があります。game.js の中身を書き換えても、住所が game.js のままなら、ブラウザの目には「前に見たものと同じ依頼」に映ります。手元のコピーが有効期間内であれば、新しい中身は取りに行かれません。

一方で、毎回サーバーに「変わっていませんか」と確認するやり方もあります。変わっていなければ中身を再送せず、変わっていれば新しい版を受け取ります。確認の分だけ通信は増えますが、古い版が残る心配は小さくなります。どちらが適切かはファイルの性質で変わります。

古い版が出る場所は一つではない

「古い版が見える」といっても、保持している場所は複数あります。切り分けないまま対策すると、効かない手を何度も打つことになります。

  • ブラウザのHTTPキャッシュ、どこで古さが生まれるか:手元に残ったHTML・JS・画像、見分けの手がかり:ハードリロードや別のブラウザで直るかどうか
  • 配信経路、どこで古さが生まれるか:作者側のサーバーや、間に入る中継、見分けの手がかり:別の端末や別の回線でも古いかどうか
  • サービスワーカー、どこで古さが生まれるか:ブラウザ内の仲介プログラムが保持する版、見分けの手がかり:開発者ツールの登録一覧に出るかどうか
  • 保存データ、どこで古さが生まれるか:localStorageやセーブの中身、見分けの手がかり:コードは新しいのに、以前の進行や設定が残るかどうか

最後の保存データは、厳密にはキャッシュとは別物です。ただし症状は「古い状態が出る」で同じなので、混同しやすい層です。新しいコードが古い形式のセーブを読んで、想定外の動きをする場合もあります。

アソバースの公式作品を見ると、この点への備えが読み取れます。13作品の共通事項として、古い保存(version1)は新しい形式(version2)へ移行すると案内されています。保存の形式に版番号を持たせ、古い版を読んだら新しい形式に直す設計です。コードの更新と保存データの更新は別々に起こるので、その両方を考えた作りだと読み取れます。

バージョン付きURLという考え方

古い版が残る問題への定番の解決策が、ファイルの名前(またはURL)に版を含める方法です。game.js を game.v2.js に変え、HTMLの参照先も書き換えます。ブラウザにとっては初めて見る住所なので、手元のコピーは使えず、必ず取りに行きます。古い game.v1.js は、もう呼ばれないだけです。

ここで要になるのが入口のHTMLです。入口の住所は変えられません。遊ぶ人が開くURLは固定だからです。そこで、入口のHTMLは「毎回確認される側」、中身のファイルは「名前で版を示し、長く使い回される側」と役割を分けます。キャッシュの恩恵(再訪問の速さ)と更新の確実さを両立するための分担です。

次の最小構成で、この仕組みを確かめられます。まず入口の index.html です。

html
<!doctype html>
<meta charset="utf-8">
<title>版の確認</title>
<p>HTMLの版: h1</p>
<p id="js"></p>
<button id="b">押した回数: 0</button>
<script src="game.v1.js"></script>

次に、同じフォルダに置く game.v1.js です。

js
const BUILD = "v1";
document.getElementById("js").textContent = "JSの版: " + BUILD;
let n = 0;
document.getElementById("b").onclick = e => {
  n++;
  e.target.textContent = "押した回数: " + n;
};

更新するときは、game.v2.js を新しく作り、中の BUILD を "v2" にします。そのうえで index.html の src を game.v2.js に、HTMLの版表示を h2 に書き換えます。画面の2行を見れば、HTMLとJSのどちらが新しく、どちらが古いかを別々に判定できます。

アソバースの仕様から読み取れること

確認済みの範囲で、この考え方と相性のよい点を挙げます。

  • 入口は直下の index.html、素材は相対パスです。 入口を固定し、その先を game.v2.js のような版付きの名前にする構成が、そのまま取れます。
  • 公式の13作品は、独立したHTMLで外部通信なしに動きます。 1つのHTMLに収まる作りなら、HTMLが更新された時点でコードも一緒に更新されます。HTMLとJSの版がずれる事故が、構造上起こりません。設計から読み取れるのはここまでです。
  • 投稿では、公開内容の更新、非公開化、削除ができます。 更新は用意された操作です。反映が遊ぶ人の画面に届くまでの時間や、配信側のキャッシュの扱いは、確認済みの案内にありません。この部分は現在の案内で確認し、実際に作品を開いて確かめてください。
  • サービスワーカーは、アップロードの対象外です。 サービスワーカーが原因の古さは、アップロード作品では考えなくてよいことになります。URLで投稿する作品は作者側の配信なので、自分のサイトの構成を確認してください。

なお、アップロード作品内の localStorage と sessionStorage は、一時メモリーの互換処理と案内されています。そのため、端末に残る保存データを進行の本体にする設計は避けたほうが安全です。長く残したいデータには、SDKによるクラウドセーブ(作品ごとにJSONで128KiBまで)が用意されています。

自分で切り分ける実験の設計書

更新が反映されないときは、次の手順で古さの居場所を特定できます。結果は読者の環境ごとに変わるので、ここでは答えを書かず、読み方だけを示します。

問い

更新後も古い版が見えるとき、それはブラウザの手元、配信経路、保存データのどこにあるのでしょうか。

準備するもの

  • 上のコードで作った、版表示つきの作品(版Aと版B)
  • 通常のウィンドウ、プライベートウィンドウ、できれば別のブラウザか別の端末
  • 開発者ツール(多くのブラウザでF12)のNetworkタブ

手順

  1. 版A(HTML: h1、JS: v1)を公開し、通常のウィンドウで開きます。2行の表示を記録します。
  2. 版B(HTML: h2、JS: v2)に更新します。ファイル名の変更と、src の書き換えを忘れていないかを確認します。
  3. 通常のウィンドウで、通常のリロードをします。
  4. 同じウィンドウで、ハードリロード(キャッシュを無視した再読み込み)をします。
  5. プライベートウィンドウで開きます。
  6. 別のブラウザか別の端末で開きます。
  7. Networkタブで「Disable cache」を有効にして開き直し、各ファイルの取得元(ディスクやメモリのキャッシュか、通信か)の表示を見ます。

記録の取り方

条件ごとに、次の項目を表に書き留めます。各条件で2回ずつ行うと、偶然の揺れを見分けられます。

  • 通常リロード(1回目・2回目)、時刻:、HTMLの版:、JSの版:、Networkでの取得元:
  • ハードリロード、時刻:、HTMLの版:、JSの版:、Networkでの取得元:
  • プライベート、時刻:、HTMLの版:、JSの版:、Networkでの取得元:
  • 別ブラウザ・別端末、時刻:、HTMLの版:、JSの版:、Networkでの取得元:

結果の読み方

  • ハードリロードやプライベートで新しくなる場合: ブラウザの手元のコピーが原因と考えられます。JSをバージョン付きの名前にし、入口のHTMLは確認されやすい状態にする方針が有効です。
  • 別の端末でも古い場合: ブラウザの外に原因があります。更新が正しく完了していないか、配信経路が古い版を保持している可能性があります。更新の操作をやり直し、反映の仕様を現在の案内で確認してください。
  • HTMLは新しいのにJSだけ古い場合: src の書き換え漏れか、ファイル名の綴りや大文字小文字の違いが疑われます。
  • HTMLが古くJSが新しい場合: HTMLだけが保持されている状態です。入口のキャッシュの扱いを見直してください。
  • 2行とも新しいのに挙動だけ古い場合: 保存データを疑います。保存の形式に版番号を持たせているかを確認してください。

起こりやすい誤り

  • 更新の操作を完了していない(下書きのまま、など)
  • ZIPの中のフォルダ階層が一段深く、直下に index.html がない
  • 版を変えたのに、参照先のパスを変え忘れている
  • 通常リロードだけで判断し、ほかの条件と比べていない
  • 切り分け中に、保存データが残ったまま古い挙動を再現して見誤る

次の行動

まず上の2つのファイルを作り、版Aを公開して、HTMLとJSの版が画面に出ることを確かめてください。次に版Bへ更新し、実験の表に従って4つの条件を比べてみてください。差が出た層が、あなたの環境で古さを保持している場所です。

そのうえで、更新が届くまでの時間や配信の扱いなど、確認済みの案内にない部分は、アソバースの現在の案内で確認してください。

参考資料

  • MDN Web Docs「HTTP キャッシュ」
  • RFC 9111「HTTP Caching」