ある朝いきなり来た「変更されてないです」
サイトの更新を納品した、その日の午後。
クライアントから届いた短いメッセージは、心臓に悪いタイプでした。
「変更されてないです」
え? いや、変わってる。
自分のPCでもスマホでも、新しいデザインがちゃんと表示されている。管理画面でも、公開ページでも、確かに反映されている。
でも先方は「変わってない」と言う。
この瞬間、頭の中では「バグ?」「反映ミス?」「ファイルの上書き失敗?」という可能性が一斉に騒ぎ始めるのに、手だけは冷静に返信しなければいけない。そしてここが”信用”の分岐点でもあるのが、精神的にキツいところです。
結論から言うと、犯人はキャッシュでした。
しかも「ブラウザキャッシュだけ」ではなく、WordPressのキャッシュプラグイン、さらに”カスタムCSS/JSの出力方法”が複雑に絡み合って起きた、よくあるのに見落としやすい事故だったのです。
この記事では、僕が実際に踏んだ手順をそのままに、
- なぜ「直したのに直ってない」が起きるのか
- どう切り分ければ最短で原因に辿り着けるのか
- WordPress(WP Fastest Cache)+ Simple Custom CSS and JS + functions.php という構成で、”二度と揉めない”状態に持っていく方法
を、実体験ベースで詳しくまとめます。
同じ構成を使っている方、あるいはキャッシュ周りで一度でも困った経験がある方にとって、今日から使える内容になっているはずです。
まず押さえる:キャッシュは「層」になっている
「キャッシュが原因です」と言われても、どのキャッシュのことなのかが分からないと対処しようがありません。
実際の現場では、キャッシュはだいたいこのような「層」として積み重なっています。
キャッシュの5つの層
1. ブラウザキャッシュ
CSS・JS・画像などの静的ファイルを、ユーザーの端末(ブラウザ)がローカルに保存するもの。一度読み込んだファイルを再利用することで、次回以降の表示を高速化します。
2. WordPressプラグインのページキャッシュ
WP Fastest CacheやW3 Total Cacheなどのプラグインが、動的に生成されるWordPressのページを静的HTMLとして保存するもの。サーバーの負荷を軽減し、表示速度を大幅に向上させます。
3. 最適化で生成されたファイル(minified CSS/JS)
キャッシュプラグインの多くは、複数のCSSやJSファイルを結合・圧縮(minify)して、別のファイルとして生成します。これも一種のキャッシュです。
4. CDNキャッシュ
CloudflareやAmazon CloudFrontなどのCDN(コンテンツ配信ネットワーク)を使っている場合、CDNのエッジサーバーにもファイルがキャッシュされます。
5. Service Worker(PWA等)
PWA(Progressive Web App)を導入している場合、Service Workerが独自のキャッシュ層を持つことがあります。これが残っていると、他のすべてのキャッシュを削除しても古いコンテンツが表示されることがあります。
今回の僕の構成
今回問題が起きたサイトの構成はこうでした。
- キャッシュプラグイン:WP Fastest Cache(無料版)
- CSS/JSの読み込み方法:メインのCSS/JSは functions.php の wp_enqueue_style() / wp_enqueue_script() で読み込み、ちょっとした調整用に Simple Custom CSS and JS(SilkyPress製) を併用
Simple Custom CSS and JSは、管理画面からCSSやJavaScriptを追記できる便利なプラグインで、僕はよく使っています。テーマファイルを直接編集せずに微調整できるのが魅力です。
ただ、この組み合わせ。便利な反面、反映トラブルが起きると「どの層にキャッシュが残っているのか?」の特定が一気に難しくなります。
現場で一番大事:まず”同じ景色”を見る(切り分けの基本パターン)
トラブル発生時、僕が最初にやったのは原因を当てにいくことではなく、再現でした。
「自分には見えている」「相手には見えていない」という状況で、いきなり原因を推測しても空振りしやすい。まずは「本当に相手と同じ状態を再現できるか」を確認するところからです。
ステップ1:シークレットウィンドウで開く(まずは最短の確認)
ブラウザのシークレットモード(Chromeなら「シークレットウィンドウ」、Firefoxなら「プライベートウィンドウ」)でサイトを開きます。
シークレットモードは既存のキャッシュやCookieを使わずにページを読み込むため、自分のブラウザに残っているキャッシュの影響を排除できます。
ここで変化が出る場合:自分の通常ブラウザにキャッシュが残っている可能性が高い
変化が出ない場合:サーバー側のキャッシュが原因の可能性
ステップ2:別端末・別回線で見る
シークレットモードでも状況が変わらない場合は、別の環境で確認します。
- スマホでWi-Fiを切ってモバイル回線で接続
- 会社PCと自宅PCなど、まったく別の環境で確認
- 可能であれば、VPNを使って別のIPアドレスからアクセス
「自分だけ見えている」「相手だけ見えていない」という食い違いをほどくには、環境を変えるのが一番早いです。
ステップ3:DevToolsで”本当に新しいファイルを取得しているか”を確認する
ここからはChromeの開発者ツール(DevTools)を使った、より詳細な確認方法です。
- Chromeでサイトを開き、F12キー(またはCtrl+Shift+I / Cmd+Option+I)でDevToolsを開く
- Networkタブを選択
- ページをリロードして、読み込まれるファイルを確認
ここでチェックするポイントは2つです。
ポイント1:CSSやJSが「(from disk cache)」になっていないか
ファイル名の横に (from disk cache) または (from memory cache) と表示されている場合、ブラウザはサーバーにリクエストを送らず、ローカルにキャッシュされた古いファイルを使っています。
ポイント2:ファイルのURLが「同じまま」になっていないか
CSSファイルのURLを確認してください。例えば:
/assets/css/style.css
/assets/css/style.css?ver=1.0.0
このURLが更新前後で変わっていない場合、ブラウザは「同じファイルだから再取得しなくていい」と判断してキャッシュを使います。
強力な確認方法:Empty Cache and Hard Reload
DevToolsを開いた状態で、アドレスバー左横のリロードボタンを長押しすると、特別なメニューが出てきます。
ここで 「Empty Cache and Hard Reload」 を選ぶと、そのサイトに関するブラウザキャッシュをすべて削除した上で、強制的にすべてのファイルを再取得します。
これで先方も新しいデザインが見えるなら「端末側のキャッシュ」、見えないなら「サーバー側・WordPress側のキャッシュ」と切り分けが進みます。
今回の”本当の犯人”:カスタムCSSが「HTMLに埋まっていた」
切り分けを進めていくと、あることに気づきました。
- 自分の環境では新しいCSSが効いている
- 先方の環境では効いていない
- でも、先方の画面でも「CSSファイル」自体は読み込まれているように見える
「じゃあCSSファイルの中身が古いのか?」と思って、読み込まれているCSSのURLを確認したら、自分と先方で同じURLでした。
ここで犯人が絞られました。
同じURLのまま差し替えると、キャッシュは”正しく”古い方を使う
ブラウザもCDNも、基本的にこう考えます。
同じURLなら、同じファイルだろう
HTTPの仕組み上、これは正しい動作です。サーバー側で同じURLのファイルを更新しても、ブラウザは「前に取得したものがあるから、それを使おう」と判断します。
だからこそ、更新を確実に伝えるための定石がキャッシュバスティング(cache busting)です。URLを変えることで、ブラウザに「これは別のファイルだ」と認識させる手法です。
問題の核心:インライン出力されていたカスタムCSS
ところが、今回僕の現場では予想外のことが起きていました。
Simple Custom CSS and JS の設定を確認したところ、カスタムCSSがインライン出力になっていたのです。
インライン出力というのは、CSSが外部ファイルではなく、HTMLの中に <style>…</style> タグとして直接埋め込まれる状態です。
<head>
...
<style>
/* ここにカスタムCSSが直接書かれている */
.custom-class {
color: red;
}
</style>
...
</head>
つまり、CSSは「CSSファイル」として存在するのではなく、ページHTMLの一部になっていたのです。
なぜこれが問題なのか
ここで何が起きるかを整理します。
- WP Fastest Cacheが、生成されたページを静的HTMLとしてキャッシュする
- そのHTMLの中には、インラインで出力されたカスタムCSSも含まれている
- Simple Custom CSS and JSでCSSを更新しても、ページキャッシュは更新されない
- 結果、ページキャッシュが残っている限り、永遠に古いCSSが表示され続ける
これが「CSSを更新したのに、先方には古いまま」の正体でした。
体感では”1番よくある”トラブルなのに、最初に見落としやすい罠です。CSSファイルを更新したつもりでも、実はCSSはファイルではなくHTMLの一部だった、というパターン。
まず応急処置:クライアントへの案内(揉めないためのテンプレート)
原因がキャッシュだと見当がついた時点で、先方にはすぐに返信しました。
経験上、この返しが早いほど空気が救われます。時間が経つほど「本当に直ったのか?」「何かミスがあったのでは?」という不信感が膨らんでしまうからです。
実際に送った文面(そのまま使えます)
お世話になっております。
ご連絡ありがとうございます。こちらの環境では更新が正常に確認できており、端末側に旧データがキャッシュとして残っている可能性が高いです。
お手数ですが、以下の手順をお試しいただけますでしょうか。
【PCの場合(Chrome)】
1. サイトを開いた状態で、キーボードの「F12」を押して開発者ツールを開く
2. 開発者ツールが開いた状態で、アドレスバー左横の「リロードボタン」を長押し
3. 表示されるメニューから「Empty Cache and Hard Reload」を選択【スマホの場合】
・別のブラウザ(普段Safariをお使いなら Chrome など)で開く
・または、Wi-Fiを切ってモバイル回線で開く【上記で改善しない場合】
・ブラウザの設定からキャッシュを削除してください
Chrome:設定 → プライバシーとセキュリティ → 閲覧履歴データを削除 → 「キャッシュされた画像とファイル」にチェック → 削除こちらでもサーバー側のキャッシュ再生成・削除を実施いたします。反映まで数分かかる場合がございますので、少々お待ちいただければ幸いです。
ご不便をおかけして申し訳ありません。
この文面のポイントは2つです。
- 「相手が悪い」とは言わない:「端末側に旧データが残っている可能性」という表現で、クライアントのせいにしない
- 「こちらも対応する」姿勢を見せる:サーバー側の作業も行うことを明記し、一方的に相手に作業を押し付けない
このバランスが、信頼関係を損なわずに問題を解決するコツです。
根本解決:二度と起きない”キャッシュバスティング運用”に整える
応急処置でその場はしのげますが、同じことが繰り返し起きるようでは運用として失敗です。
ここからが本題。「更新が見えない」を、仕組みとして潰していきます。
1. functions.php:enqueueのバージョンをfilemtimeで自動更新する
WordPressの wp_enqueue_style() と wp_enqueue_script() には、第4引数としてバージョン番号を指定できます。このバージョン番号は、読み込まれるURLのクエリパラメータとして付与されます。
wp_enqueue_style('my-style', get_stylesheet_uri(), [], '1.0.0');
// → /wp-content/themes/my-theme/style.css?ver=1.0.0
これがキャッシュバスティングの王道です。バージョンが変われば別のURLになるため、ブラウザは新しいファイルとして取得します。
ただし、手動で ver=1.0.1 → ver=1.0.2 と上げていく運用は、だいたい忘れます。人間はそういうものです。
そこで、ファイルの更新日時(タイムスタンプ)を使って、自動的にバージョンが変わるようにします。
add_action('wp_enqueue_scripts', function () {
// CSSの読み込み
$css_path = get_stylesheet_directory() . '/assets/css/app.css';
$css_uri = get_stylesheet_directory_uri() . '/assets/css/app.css';
wp_enqueue_style(
'my-app-style', // ハンドル名
$css_uri, // ファイルのURL
[], // 依存関係
file_exists($css_path) ? filemtime($css_path) : null // バージョン(更新日時)
);
// JSの読み込み
$js_path = get_stylesheet_directory() . '/assets/js/app.js';
$js_uri = get_stylesheet_directory_uri() . '/assets/js/app.js';
wp_enqueue_script(
'my-app-script', // ハンドル名
$js_uri, // ファイルのURL
[], // 依存関係
file_exists($js_path) ? filemtime($js_path) : null, // バージョン(更新日時)
true // フッターで読み込む
);
});
filemtime() はPHPの組み込み関数で、ファイルの最終更新日時をUnixタイムスタンプ(秒単位の数値)で返します。
これにより、出力されるURLはこうなります。
<link rel="stylesheet" href="/assets/css/app.css?ver=1736899200">
<script src="/assets/js/app.js?ver=1736899200"></script>
ファイルを更新するたびにタイムスタンプが変わるので、URLも自動的に変わります。ブラウザは「別のファイルだ」と認識して、キャッシュを使わず新しいファイルを取得してくれます。
補足:file_exists()でチェックする理由
filemtime() は、ファイルが存在しない場合に警告を出します。子テーマ構成などでファイルパスが変わる可能性がある場合、file_exists() で存在確認してから filemtime() を呼ぶのが安全です。
2. Simple Custom CSS and JS:外部ファイル出力を基本にする
Simple Custom CSS and JSには、出力方法として「インライン」と「外部ファイル」の2つがあります。
今回の問題の直接の原因は「インライン出力」だったので、外部ファイル出力を基本設定にすることをおすすめします。
設定方法:
- WordPress管理画面の「カスタムCSS & JS」→ 対象のカスタムコードを編集
- 「Options」セクションで「Linking type」を確認
- 「External File」を選択して保存
外部ファイル出力にするメリットはシンプルです。
- インラインの場合:CSSがページHTMLの一部になるため、ページキャッシュの中に含まれる。CSSだけ更新してもページキャッシュが残っている限り反映されない。
- 外部ファイルの場合:CSSは独立したファイルとして読み込まれる。ファイル単位でキャッシュが管理されるため、更新の反映がシンプル。
もしインライン出力が必要な事情がある場合(クリティカルCSSの埋め込みなど)は、運用ルールを明確にします。
ルール:カスタムCSS/JSを更新したら、必ずページキャッシュを削除する
このルールが曖昧だと、また同じトラブルが起きます。
3. WP Fastest Cache:削除すべきは「ページキャッシュ」だけではない
WP Fastest Cacheには、実は複数の種類のキャッシュがあります。
- ページキャッシュ:生成されたHTMLページのキャッシュ
- Minified CSS/JS:結合・圧縮されたCSS/JSファイル
更新時には、この両方を削除する必要があります。
削除手順:
- WordPress管理画面の「WP Fastest Cache」を開く
- 「Delete Cache」タブを選択
- 「Delete Cache」ボタンでページキャッシュを削除
- 「Delete Cache and Minified CSS/JS」ボタンでminifyされたファイルも削除
「CSSを変えたのに反映されない」系のトラブルは、ページキャッシュよりもminify生成物が古いまま残っているせいで起きることも多いです。
WP Fastest Cacheは、複数のCSSファイルを1つに結合し、圧縮して別のファイルとして保存します。元のCSSを更新しても、この結合・圧縮後のファイルが再生成されなければ、ブラウザには古いCSSが配信され続けます。
「キャッシュさせない」は最終手段。基本は”キャッシュさせて、更新だけ確実に伝える”
ここで一つ、運用の考え方を整理しておきます。
キャッシュ問題に直面すると、つい「キャッシュを無効にしてしまえばいいのでは」と思いがちです。しかしこれは避けた方がいい。
なぜキャッシュは必要なのか
静的ファイル(CSS・JS・画像など)は、キャッシュさせた方がサイトが速くなります。
キャッシュを無効にすると、ユーザーがページを開くたびにすべてのファイルをサーバーから再取得することになります。これはサーバーの負荷を増やし、ユーザーの表示速度を落とし、モバイル回線のデータ通信量も増やします。
だから基本方針はこうです。
普段はキャッシュを効かせておき、更新があった瞬間だけURLを変える(キャッシュバスティング)
HTTPヘッダーの Cache-Control について
HTTPには、キャッシュの挙動を制御するための Cache-Control ヘッダーがあります。似た名前の指示がいくつかあるので、違いを整理しておきます。
no-cache:キャッシュへの保存は許可。ただし使う前に必ずサーバーに再検証(更新確認)すること
no-store:キャッシュに保存しないこと。履歴にも残さない
max-age=0:キャッシュの有効期限を0秒に設定。実質的に毎回再検証が必要
「キャッシュを完全無効」にするなら no-store ですが、これはパフォーマンスに大きく影響します。通常は max-age で適切な有効期限を設定し、更新時はURLを変える(キャッシュバスティング)のがベストプラクティスです。
画像が変わらない時の、いちばん堅い対策
画像はCSS/JS以上に「同名差し替え」でキャッシュが残りやすいファイルです。
よくあるパターン:
- hero.jpg を新しい画像で上書きアップロードしたのに、古い画像が表示される
- 自分のPCでは新しい画像が見えるのに、クライアントには古い画像が見えている
なぜ画像のキャッシュは残りやすいのか
画像ファイルは、CSSやJSよりも長期間キャッシュされる設定になっていることが多いです。理由は単純で、画像は一般的に頻繁に更新されないから。サーバー側の設定でも、画像の max-age は長めに設定されていることが多いです。
確実な解決策
画像の更新を確実に反映させる方法は2つです。
方法1:ファイル名を変える(推奨)
旧: hero.jpg
新: hero-20260115.jpg
日付やバージョン番号をファイル名に含めることで、確実に別のファイルとして認識されます。HTMLやCSS側の参照パスも更新する必要がありますが、これが最も確実な方法です。
方法2:クエリパラメータを付ける
<img src="hero.jpg?v=20260115" alt="...">
ファイル名は変えず、URLにクエリパラメータを追加します。ただし、一部のCDNやプロキシはクエリパラメータを無視してキャッシュすることがあるため、方法1より確実性は劣ります。
避けるべきパターン
「同じファイル名で上書きアップロード」は避ける
WordPressのメディアライブラリで同名ファイルをアップロードしても、サーバー上の古いファイルが上書きされるとは限りません(WordPressはファイル名に連番を付けて別ファイルとして保存することがあります)。
また、仮に上書きされたとしても、キャッシュが効いている限り「正しく古い画像」が返されます。
仕上げ:僕の「再発防止チェックリスト」
ここまでの内容を、実際の運用で使えるチェックリストとしてまとめます。
更新前(制作・開発時のルール)
- CSS/JSは wp_enqueue_style() / wp_enqueue_script() で読み込む(HTMLに直書きしない)
- バージョンは filemtime() で自動更新する設定にする
- Simple Custom CSS and JSは外部ファイル出力を基本にする
- インライン出力を使う場合は、更新時のキャッシュ削除手順を明文化する
- 画像の差し替えは「同名上書き」を避け、ファイル名を変更する
更新後(納品時の確認手順)
- WP Fastest Cacheで「Delete Cache」を実行してページキャッシュを削除
- WP Fastest Cacheで「Delete Cache and Minified CSS/JS」を実行してminifyファイルも削除
- シークレットウィンドウで更新を確認
- 別回線(モバイル回線など)で更新を確認
- CDNを使っている場合はCDNのキャッシュもパージ
- クライアントへ「強制再読み込み手順」を案内(テンプレート文面を用意しておく)
トラブル発生時の切り分け手順
- シークレットウィンドウで開く → 変化あり?
- 別端末・別回線で確認 → 変化あり?
- DevToolsのNetworkタブで「from cache」を確認
- 「Empty Cache and Hard Reload」で確認
- 上記で解決しなければサーバー側キャッシュを疑う
補足:他のキャッシュプラグインを使っている場合
この記事ではWP Fastest Cacheを例にしましたが、他のキャッシュプラグインでも基本的な考え方は同じです。
W3 Total Cache
- 「Performance」→「Page Cache」→「empty cache」でページキャッシュ削除
- 「Performance」→「Minify」→「empty cache」でminifyキャッシュ削除
WP Super Cache
- 「設定」→「WP Super Cache」→「キャッシュを削除」
- このプラグインはminify機能を持たないため、ページキャッシュの削除だけでOK
LiteSpeed Cache
- 「LiteSpeed Cache」→「ツールボックス」→「すべてをパージ」
- CSS/JSの最適化をしている場合は「CSSキャッシュをパージ」「JSキャッシュをパージ」も実行
Autoptimize(最適化専用プラグイン)
キャッシュプラグインと併用していることが多いAutoptimizeは、独自にminifyファイルを生成します。
- 「設定」→「Autoptimize」→「キャッシュを削除」
キャッシュプラグインとAutoptimizeを併用している場合は、両方のキャッシュを削除する必要があります。
おわりに:この事故は”技術”より”運用”で減らせる
今回の経験で痛感したのは、キャッシュ問題というのは、
- 技術的には「URLを変える」で解決できる
- でも現場では「誰が」「いつ」「どこまで」キャッシュを消すかが曖昧なまま運用されていて、そこで揉める
ということでした。
技術的な解決策(filemtimeを使う、外部ファイル出力にする、など)を導入しても、それだけでは不十分です。運用ルールとして「カスタムCSS/JSを触ったらキャッシュ削除(minify含む)」を明文化し、クライアント向けの案内文もテンプレート化しておく。
僕はこれを保守契約の手順書に追記しました。
これだけで、納品直後の不毛なやり取りが激減しました。
「直したのに直ってないと言われる」というのは、技術者としてはかなり心臓に悪い体験です。でも、仕組みを理解して運用を整えれば、確実に防げる種類のトラブルでもあります。
もしあなたも同じような構成(WP Fastest Cache + functions.php + Simple Custom CSS and JS)を使っているなら、今日からでもこの運用に寄せるだけで、だいぶ平和になるはずです。
この記事が、同じ苦しみを味わっている誰かの助けになれば幸いです。


コメント