
JS・CSSが反映されない|キャッシュバスティングの仕組みと手順
「ボタンの位置、直したって聞いたんですけど、まだずれてますよ」
CSSを1行直してリリースして、自分のブラウザでは確かに直っている。なのに、問い合わせの電話の向こうでは崩れたまま。「お手数ですが、Ctrl+F5で再読み込みしていただけますか」とお願いして、その場はなんとか収まる——こんなやり取り、覚えがありませんか。
これは作業ミスではありません。ブラウザやCDNが、前に読んだJS・CSSをちゃんと覚えているから起きることです。キャッシュは表示を速くするための大事な仕組みなので、止めてしまうのは得策ではありません。必要なのは、「新しい版が出たよ」とブラウザに気づいてもらう仕掛けのほうです。
この記事では、その仕掛け=キャッシュバスティングを、一人運用でも続けられる手順に分けて一緒に整理していきます。
結論:やることは3つです。①HTMLは「毎回確認」、JS・CSSは「長く覚えてよい」と、キャッシュの持たせ方を分ける、②JS・CSSを変えたら、ファイル名かURLが変わるようにする(app.3f9a1c.cssやapp.css?v=20261002など)、③反映の確認は自分のブラウザではなく、HTMLが新しいURLを指しているかで確かめる。利用者に再読み込みをお願いしなくても、次にページを開いた時点で新しいJS・CSSが届くようになります。
なお、設定の書き方はWebサーバー・CDN・フレームワークやそのバージョンで違います。ここで挙げる例は考え方をつかむためのものなので、検証環境で挙動を確かめてから本番に入れてください。
何が起きているのか:ブラウザは「URLが同じなら同じファイル」と考える
ブラウザは、一度読み込んだJS・CSS・画像を手元に保存しておき、次に同じURLを見に行くときはそれを使います。サーバーが返す Cache-Control ヘッダーで「何秒まで覚えていてよいか」(max-age)が指定されていれば、その間はサーバーに問い合わせすらしないことがあります。
つまり、こういうことが起きます。
- サーバー上の
style.cssは新しくなった - でもHTMLが指しているURLは、前と同じ
/css/style.css - 利用者のブラウザは「このURLなら持っている」と、保存してある古いほうを使う
あなたのブラウザで直って見えるのは、開発中に何度も再読み込みしていたり、開発者ツールでキャッシュを無効にしていたりするからです。自分の画面が正しくても、利用者の画面が同じとは限らない。ここが、この問題のいちばん分かりにくいところです。
CDNやリバースプロキシを挟んでいる場合は、ブラウザの手前にもう1か所「覚えている場所」があります。古い値がどこに残るかを順番に切り分ける方法は「直したのに直らない」を切り分ける|キャッシュを疑う順番でまとめているので、いま目の前で起きている問題の調査はそちらも合わせて見てみてください。この記事では、そもそも次から起きにくくする側の手順を扱います。
手順1:HTMLとJS・CSSで、キャッシュの持たせ方を分ける

最初に決めるのは、ファイルの種類ごとに、どれくらい覚えておいてもらうかです。考え方はシンプルで、2種類に分けます。
- HTML(入口のページ):毎回サーバーに確認してもらう。
Cache-Control: no-cacheを付けると、ブラウザは保存していても使う前にサーバーへ「変わっていませんか」と確認します(no-cacheは「保存しない」ではなく「毎回確認する」という意味です)。変わっていなければ短い返事(304 Not Modified)で済むので、重くなりすぎることはありません。 - JS・CSS・画像(名前が変わるファイル):長く覚えてもらってよい。中身を変えるときは必ずファイル名かURLを変える(手順2)という約束があれば、古いものを覚えていても困りません。
Cache-Control: max-age=31536000, immutable(1年)のような長い指定がよく使われます。
nginxなら、たとえばこんな形です。
# HTMLは毎回確認してもらう
location ~* \.html$ {
add_header Cache-Control "no-cache";
}
# ビルドツールが出力する、ファイル名にハッシュが入ったJS・CSSの置き場は長く覚えてもらう
location /assets/ {
add_header Cache-Control "public, max-age=31536000, immutable";
}
ここで大切なのは、長く覚えてもらう指定は「名前が変わる仕組み」とセットで入れることです。名前が変わらない style.css に1年の指定を付けると、かえって反映されない期間が長くなってしまいます。まずは今の設定を確かめるところから始めましょう。
# いまJS・CSS・HTMLにどんなキャッシュ指定が付いているかを見る
curl -sI https://example.com/ | grep -i -E "cache-control|etag|last-modified"
curl -sI https://example.com/css/style.css | grep -i -E "cache-control|etag|last-modified"
PHPなどでHTMLをアプリが出している場合は、Webサーバーではなくアプリやフレームワーク側でヘッダーを付けていることもあります。どこで付いているかを一度たどっておくと、あとで迷いません。
手順2:JS・CSSを変えたら、URLが変わるようにする
ここが本題です。キャッシュバスティングとは、中身を変えたときにURLも変えて、ブラウザに「これは初めて見るファイルだ」と思ってもらうことです。やり方は大きく3つあります。自分の現場に合うものを1つ選べば十分です。
やり方A:ビルドツールでファイル名にハッシュを入れる(いちばん手堅い)
ViteやwebpackなどのビルドツールでJS・CSSをまとめている現場なら、中身から計算したハッシュをファイル名に入れる機能があります。Viteは本番ビルドで標準的に assets/index-BkYZ3a8q.js のような名前を出力し、HTML内の参照も自動で書き換えてくれます。webpackなら出力名に [contenthash] を指定します。
中身が変わればハッシュが変わり、変わらなければ同じ名前のまま。変えたファイルだけが新しく読み込まれ、変えていないファイルはキャッシュがそのまま効くので、いちばん無駄がありません。
すでにビルドツールを使っているのに、設定でハッシュを外している現場もあります。まずは出力されたファイル名を見てみてください。
やり方B:URLにバージョンを付ける(ビルドツールがない現場で)
ビルドツールがなく、HTMLやテンプレートに <link> を直接書いている現場では、URLの後ろにバージョンを付ける方法が手軽です。
<link rel="stylesheet" href="/css/style.css?v=20261002">
<script src="/js/app.js?v=20261002"></script>
数字を手で書き換えると、ほぼ確実に「上げ忘れ」が起きます。PHPのテンプレートなら、ファイルの更新時刻を自動で付けると手間がなくなります。
<?php $v = filemtime(__DIR__ . '/css/style.css'); ?>
<link rel="stylesheet" href="/css/style.css?v=<?= $v ?>">
ただし、この方法には気をつけたい点が2つあります。
- CDNの設定によっては、
?から後ろを無視してキャッシュすることがある。クエリ文字列をキャッシュの区別に使わない設定だと、?v=を変えても古いものが返ります。使っているCDNの設定(キャッシュキーにクエリ文字列を含めるか)を一度確認しておきましょう。 - 複数サーバー構成では、サーバーごとに更新時刻がずれることがある。
filemtime()の値が台ごとに違うと、振り分け先によってURLが変わり、キャッシュが効きにくくなります。その場合は、更新時刻ではなくリリース番号やコミットのIDを1か所の設定に書いて使うほうが安定します(複数サーバーへの反映漏れを防ぐ)。
やり方C:ファイル名そのものを変える
style.css を style.v2.css にする、のようにファイル名を手で変える方法もあります。仕組みはいらない一方で、参照している箇所をすべて書き換える必要があり、書き換え漏れが起きやすいやり方です。ほかの2つが難しいときの、一時的な手段と考えておくのがよいと思います。
古いファイルはすぐに消さない
ファイル名を変える方式(AとC)では、リリース直後に古いファイルを消さないようにしておきます。古いHTMLを開いたままの利用者や、CDNにまだ古いHTMLが残っている間は、古いJS・CSSを読みに来るからです。消してしまうと、その間だけ画面が崩れたり、ボタンが動かなかったりします。「次のリリースのときに、前々回の分を消す」くらいの間隔で十分です。
手順3:反映の確認は「HTMLが新しいURLを指しているか」で見る
リリースしたら、確認です。ここでも、自分のブラウザで見て直っていれば完了、にはしないのがポイントです。
確かめるのは2つだけです。
# 1) 本番のHTMLが、新しいURL(ハッシュ・バージョン)を指しているか
curl -s https://example.com/ | grep -o -E 'href="[^"]+\.css[^"]*"|src="[^"]+\.js[^"]*"'
# 2) その新しいURLのファイルが、ちゃんと返ってくるか(200になるか)
curl -sI "https://example.com/assets/index-BkYZ3a8q.css" | head -1
1で古いURLのままなら、HTML自体が古い(テンプレートの反映漏れ、HTMLのキャッシュ、CDN)ということです。2で 404 なら、JS・CSSのファイルが反映先に届いていません。どちらで止まっているかが分かれば、次の一手が決まります。
ブラウザで見るなら、シークレットウィンドウや開発者ツールの「ネットワーク」タブで、読み込まれているファイル名を見るのが確実です。
具体例:「スーパーリロードしてください」をやめた現場
ある社内システムの保守担当の、よくある流れです。
- 以前:画面は社内の業務システムで、CSSとJSはテンプレートに
/css/style.cssと直接書いてあった。Webサーバーの設定で、静的ファイルに7日間のキャッシュ指定。CSSを直すたびに「画面が崩れている」という問い合わせが数件来て、そのつど再読み込みをお願いしていた。 - 手順を変えたあと:テンプレートの参照に
?v=とリリース番号を付け、番号はリリース手順で更新する設定ファイル1か所から読むようにした。HTMLにはCache-Control: no-cacheを付け、反映後はcurlでHTMLのURLが新しい番号になっているかを確かめる。CSS変更のあとの問い合わせは来なくなり、手順書から「利用者に再読み込みを依頼」の1行が消えた。
変わったのは、利用者の操作でも、担当者の注意力でもありません。「新しい版ができた」ことがURLで伝わるようになったことです。
影響:URLで新しい版を伝えると、何が変わるか
- 「直ってない」の問い合わせが減る:利用者が何もしなくても、次に開いたときに新しいJS・CSSが届きます。
- キャッシュを止めずに済む:表示の速さはそのままに、反映だけ確実にできます。
- リリース後の確認が短くなる:HTMLのURLを見るだけで、「全員に新しい版が届く状態か」を判断できます。
- ロールバックしやすくなる:古い版のファイルを残しておけば、HTMLを戻すだけで前の画面に戻せます(ロールバック手順の作り方)。
明日やること
- 今のキャッシュ指定を
curl -sIで見る。HTMLとJS・CSSそれぞれに、どんなCache-Controlが付いているかを書き出します。 - JS・CSSの参照のされ方を確かめる。ファイル名にハッシュが入っているか、
?v=が付いているか、何も付いていないかを見ます。 - 自分の現場に合うやり方を1つ選ぶ。ビルドツールがあればA、なければBから始めるのがおすすめです。
- リリース手順書に確認コマンドを1行足す。「HTMLが新しいURLを指しているか」を
curlで見る行です(リリース手順書の作り方)。 - CDNを使っていれば、クエリ文字列の扱いを確認する。
?v=方式を選ぶなら、ここだけは先に見ておきましょう。
今日やるのは、1の「今のキャッシュ指定を見る」だけでも十分です。どこで覚えられているかが分かるだけで、次のリリースの見通しがずいぶん変わります。
JS・CSS反映(キャッシュバスティング)チェックリスト
- HTMLとJS・CSSに付いている
Cache-Controlを確認したか - HTMLは毎回確認(
no-cacheなど)、または短いキャッシュになっているか - JS・CSSは、中身を変えたときにファイル名かURLが変わる仕組みになっているか
- 長いキャッシュ指定(
max-age長め・immutable)は、名前が変わるファイルにだけ付けているか -
?v=の値は手書きではなく、更新時刻やリリース番号から自動で入るか - 複数サーバー構成で、URLのバージョンが全台で同じになるか
- CDNを使っている場合、クエリ文字列をキャッシュの区別に使う設定か確認したか
- ファイル名を変える方式で、古いファイルをリリース直後に消していないか
- 反映後、本番のHTMLが新しいURLを指していることを
curlなどで確かめたか - 新しいURLのJS・CSSが
200で返ることを確かめたか
全部そろっていなくても大丈夫です。「HTMLは毎回確認」と「JS・CSSはURLが変わる」の2つがあれば、反映されない問題の多くは起きにくくなります。
よければ、こちらも
- 「直したのに直らない」を切り分ける|キャッシュを疑う順番:いま目の前の「古いまま」を調べたいときに。
- 複数サーバーへの反映漏れを防ぐ|デプロイ手順と1台ずつの確認方法:キャッシュではなく、そもそも1台に届いていないときに。
- リリース手順書の作り方|一人でも迷わない最低限の項目テンプレート:確認コマンドを手順に組み込むために。
- ロールバック手順の作り方|すぐ戻せるリリース設計を一人運用で:古い版のファイルを残しておく理由と合わせて。

「スーパーリロードしてください」とお願いしてきたのは、利用者の画面をなんとか直そうとしてきた証拠です。その気づかいを、これからは仕組みのほうに任せてしまいましょう。
キャッシュは敵ではなく、サービスを速くしてくれる味方です。URLをひとつ変えるだけで、その味方と上手に付き合えるようになります。まずは今の設定を、curl で一度のぞいてみてください。
ほかの実務ヒントは記事一覧からどうぞ。保守運用の小さな備えを、メールでも少しずつお届けしています。