独立系サイトのキャッシュ戦略:ページキャッシュ、オブジェクトキャッシュ、ブラウザキャッシュの設定

独立系サイトのパフォーマンス最適化で、キャッシュは投資対効果が最も高い一手です。ページキャッシュを設定するだけで、LCPを4秒から1秒以内に短縮できます。3層のキャッシュはそれぞれ役割が異なります。ページキャッシュはHTML、オブジェクトキャッシュはデータベースクエリ結...

三层缓存:每层管一段,不能混Three cache layers: each owns a segment, don’t mix 页面缓存管HTML · 对象缓存管数据库查询 · 浏览器缓存管静态资源Page=HTML · Object=DB queries · Browser=static assets ① 页面缓存 Page Cache —— 把整个HTML存起来,下次直接返回① Page Cache — store full HTML, return it next time 缓存时间:静态页面1小时至1天 · 表单页/wp-admin永不缓存TTL: static pages 1h-1d · forms/wp-admin never cached ② 对象缓存 Object Cache —— 把数据库查询结果存到Redis/Memcached② Object Cache — store DB query results in Redis/Memcached TTL:对象缓存12小时至7天 · 内容更新时主动失效TTL: 12h-7d · invalidate on content update ③ 浏览器缓存 Browser Cache —— 用Cache-Control头让访客本地存静态资源③ Browser Cache — use Cache-Control header to cache locally TTL:图片30天至1年 · CSS/JS 7天 · 文件名带hash自动失效TTL: images 30d-1y · CSS/JS 7d · hash-based invalidation 优先级:页面缓存 > 对象缓存 > 浏览器缓存(先配最值钱的)Priority: page > object > browser (do the highest-value first) 只配页面缓存就能让LCP从4秒降到1秒以内,对象缓存是加分项Page cache alone cuts LCP from 4s to under 1s; object cache is bonus 反直觉:缓存不是越多越好,表单页缓存了会泄露别人的提交Counter-intuitive: caching forms leaks other users’ submissions 询盘表单、wp-admin、购物车、登录后页面必须no-cacheInquiry forms, wp-admin, cart, logged-in pages must be no-cache 浏览器缓存不是越长越好:用文件hash解决”改了看不到”Browser TTL isn’t longer=better: use file hashes for updates
独立系サイトの3層キャッシュ戦略:ページキャッシュ、オブジェクトキャッシュ、ブラウザキャッシュの役割分担とTTL設定

独立系サイトのキャッシュ戦略:ページキャッシュ、オブジェクトキャッシュ、ブラウザキャッシュの設定

結論から言うと:独立系サイトのパフォーマンス最適化で、キャッシュは投資対効果が最も高い一手です。ページキャッシュを設定するだけで、LCPを4秒から1秒以内に短縮できます。3層のキャッシュはそれぞれ役割が異なります。ページキャッシュはHTML、オブジェクトキャッシュはデータベースクエリの結果、ブラウザキャッシュは静的リソースを担当します。TTLの推奨値は、静的ページキャッシュが1時間から1日、オブジェクトキャッシュが12時間から7日、画像のブラウザキャッシュが30日から1年です。直感に反する判断ですが、キャッシュは多ければ多いほど良いわけではありません。問い合わせフォーム、wp-admin、ログイン後のページは必ずno-cacheにしてください。そうしないと、ユーザーに他人が送信した内容が表示されてしまいます。設定の順序はページキャッシュ、次にオブジェクトキャッシュ、最後にブラウザキャッシュです。逆にしないでください。

1. 3層キャッシュのそれぞれの役割

独立系サイトのキャッシュは3層に分けられ、各層が異なる問題を解決します。一緒にしないでください。ページキャッシュはHTMLのレンダリング結果全体を静的ファイルとして保存し、次に同じURLへアクセスがあったときはファイルをそのまま返します。PHPもデータベースも実行する必要はありません。オブジェクトキャッシュはデータベースクエリの結果をメモリ(RedisやMemcachedなど)に保存し、次に同じクエリがあればメモリから直接取得します。MySQLへ再び問い合わせる必要はありません。ブラウザキャッシュはHTTPレスポンスヘッダーで訪問者のブラウザに「このファイルはローカルに保存しておいてください」と伝え、次回のアクセス時はローカルディスクから直接読み込みます。

3層のパフォーマンス効果には大きな差があります。ページキャッシュの効果が最大です。PHPの実行とデータベースクエリをまるごとスキップし、LCPを4秒以上から1秒以内に短縮できます。オブジェクトキャッシュの効果は中程度です。データベースクエリの時間を省くだけなので、ページキャッシュがすでにヒットしていれば、オブジェクトキャッシュはそもそも発動しません。ブラウザキャッシュの効果はリピーター向けです。初回アクセスでは必ずダウンロードが必要ですが、2回目以降はローカルから読み込むため、再訪問時の速度がはっきり向上します。

キャッシュ層 保存するもの 保存場所 パフォーマンス効果 代表的なツール
ページキャッシュ HTMLページ全体 サーバーのディスクまたはメモリ 最大、LCPが60%から80%低下 WP Rocket、LiteSpeed Cache
オブジェクトキャッシュ データベースクエリの結果、PHPオブジェクト RedisまたはMemcached 中程度、DBクエリ時間が50%低下 Redis、Memcached
ブラウザキャッシュ CSS/JS/画像 訪問者のブラウザ内 リピーター、2回目のアクセスが速い Cache-Controlレスポンスヘッダー
CDNキャッシュ 静的リソース+一部のHTML 世界各地のエッジノード 地域間の遅延が50%以上低下 Cloudflare、Akamai

多くの人はまずRedisやMemcachedをいじりたがりますが、B2Bの自社サイトならページキャッシュを正しく設定するだけで性能問題の80%は解決します。オブジェクトキャッシュは加点要素で、月間UVが5万を超えたときやデータベースクエリが遅くなってきたときに導入するのが適切です。CDNと3層キャッシュの関係はCDNによるグローバル高速化とノード選定、性能指標の合格ラインはCore Web Vitalsの最適化。

二、ページキャッシュ:HTMLをどう保存すれば失敗しないか

ページキャッシュは最も価値のある層であり、最も設定を誤りやすい層でもあります。設定ミスの典型的な症状は、訪問者が問い合わせを送信した後に表示されるのが前の訪問者の送信内容だったり、ログイン後なのに未ログイン状態のページが表示されたりすることです。原因は、動的コンテンツを含むHTMLまで静的ファイルとして保存してしまうことにあります。

正しい方法は、URLとユーザー状態で2種類に分けることです。公開静的ページ(トップページ、製品ページ、ブログ記事ページ)はページキャッシュの対象にし、動的ページ(問い合わせフォーム送信後、wp-admin管理画面、ログイン後のページ、ショッピングカート)は必ずキャッシュをスキップさせます。WordPressのキャッシュプラグイン(WP Rocket、LiteSpeed Cache)はどちらも「URLの除外」と「Cookieによるキャッシュスキップ」のルールに対応しています。

ページの種類 キャッシュの可否 キャッシュ期間 除外ルール
トップページ、製品一覧ページ キャッシュ 1時間から4時間 ログインユーザーを除外
ブログ記事ページ キャッシュ 12時間から1日 コンテンツ更新時に手動で更新
問い合わせフォームページ キャッシュしない 設定なし 常にオリジンへ
フォーム送信完了ページ キャッシュしない 設定なし session cookie付き
wp-admin管理画面 キャッシュしない 設定なし 管理者cookieを除外
検索結果ページ 短時間キャッシュまたはキャッシュなし 5分 検索語ごとのキャッシュを避ける

シナリオ例:産業機械メーカーの公式サイトの製品詳細ページで、URLが /products/cnc-machine-a/ だとします。このページの内容は数か月に一度しか変わらないので、1日キャッシュしても全く問題ありません。しかし問い合わせフォームの「送信完了」ページのURLは /inquiry-thank-you/ で、このページは訪問者ごとに自分が送信した確認情報が表示されるべきなので、絶対にキャッシュしてはいけません。WP Rocketでは、/inquiry-thank-you/ と /wp-admin/* をNever Cache URL(s)リストに追加します。

キャッシュの無効化もページキャッシュの鍵です。ブログ記事を1本修正したとき、どうやって本番環境にすぐ反映させるか。方法は2つあります。1つは公開時に手動で全キャッシュをPurgeする方法、もう1つは「URL単位の更新」でその記事だけを消す方法です。WP Rocketは記事更新時に関連キャッシュを自動で更新しますが、多くのカスタムページでは自動でトリガーされないため、公開後にPurgeをクリックする習慣をつける必要があります。环形山は越境サイトにおいてキャッシュの無効化と公開フローを連動させています。詳しくはcraterの技術スタックと4つの自社開発プラグイン。

三、オブジェクトキャッシュ:データベースクエリの結果をどう再利用するか

ページキャッシュがヒットしているときは、PHPもデータベースも動かないので、オブジェクトキャッシュも当然使われません。しかしページキャッシュがミスしたとき、たとえば新しい記事を公開した直後や、キャッシュすべきでない動的ページにアクセスしたときは、PHPがデータベースに問い合わせます。WordPressのページは平均12〜20回データベースに問い合わせ、そのたびにディスクIOが発生します。遅いクエリが積み重なると、ページの表示に1秒以上かかることもあります。

オブジェクトキャッシュは、こうしたクエリ結果をRedisやMemcachedに保存し、次に同じクエリが来たらメモリから直接読み取ります。Redisを1台のサーバーにインストールし、WordPress側はRedis Object Cacheプラグインで接続するだけで、コードを書き換える必要はありません。正しく設定すれば、データベースのクエリ時間を50%以上削減でき、ページキャッシュのミス時の応答時間も1.5秒から0.5秒以内に短縮できます。

指標 オブジェクトキャッシュなし Redisオブジェクトキャッシュあり 改善幅
ページあたりのデータベースクエリ回数 12〜20回/ページ 2〜5回/ページ 70%〜80%削減
1回のクエリにかかる時間 5〜20ミリ秒(ディスクIO) 0.1〜1ミリ秒(メモリ) 10〜50倍高速
ページキャッシュのミス時の応答時間 1〜1.5秒 0.3〜0.5秒 50%以上削減
MySQLのCPU使用率 ピーク時80%以上 ピーク時30%〜40% 半分以上削減

オブジェクトキャッシュは必ず導入すべきものではありません。月間UVが1万以下のB2Bサイトならデータベースの負荷はそれほど大きくなく、ページキャッシュがすでに95%のリクエストを処理しているため、Redisを導入しても効果はあまり見込めません。では、いつ導入すべきか。判断の目安は2つあります。1つ目は、MySQLのスロークエリログで1秒を超えるクエリが1日に100件以上あること。2つ目は、管理画面で記事を編集して保存したあと、フロント側に反映されるまで数分かかること(オブジェクトキャッシュが正しく無効化されていない状態)です。データベースのスロークエリとインデックスの最適化については独立サイトのデータベーススロークエリとインデックス最適化。

四、ブラウザキャッシュ:静的リソースのキャッシュヘッダーの設定方法

ブラウザキャッシュは3層の中で最も設定が簡単な層です。サーバー側のプログラムは一切関係なく、Webサーバー(NginxまたはApache)にレスポンスヘッダーを数行追加するだけです。仕組みはこうです。サーバーがCSS、JS、画像を返すときに、HTTPレスポンスヘッダーにCache-Control: max-age=2592000を追加し、ブラウザに「このファイルは30日間ローカルに保存してください」と伝えます。30日以内に再アクセスしてもサーバーへのリクエストは発生しません。

ただし、ここには古典的な矛盾があります。ブラウザキャッシュの期間を長く設定するとユーザーのアクセスは速くなりますが、CSSやJSを変更してもユーザーのローカルには古いバージョンが残り、表示が崩れてしまいます。解決策はキャッシュ期間を短くすることではなく、ファイル名にバージョン番号やハッシュを付けることです。たとえば style.css?v=2 や style.abc123.css のようにします。ファイルの中身が変わればURLも変わるので、ブラウザは自動的に再ダウンロードします。

リソース種別 Cache-Controlの推奨設定 max-age 更新方法
画像(jpg/png/webp) public, max-age=31536000 1年 ファイル名にハッシュを付け、画像を差し替えたら名前も変更
フォントファイル public, max-age=31536000 1年 フォントは変更がなければそのままでOK
CSSスタイルシート public, max-age=604800 7日 ファイル名にバージョン番号?ver=2
JavaScript public, max-age=604800 7日 ファイル名にバージョン番号?ver=2
HTMLページ no-cache, must-revalidate キャッシュしない 毎回オリジンで検証
favicon.ico public, max-age=604800 7日 ほとんど変更なし

直感に反する判断:多くの人はキャッシュ期間が長いと変更がユーザーに届かないと恐れ、すべてのリソースのmax-ageを1時間に設定します。その結果、訪問者は毎回すべてのCSSとJSを再ダウンロードし、ファーストビューがかえって遅くなります。正しい方法は思い切って長く設定し(画像1年、CSS/JS 7日)、ファイルハッシュやバージョン番号で更新に対応することです。WordPressのwp_enqueue_script関数は自動でバージョン番号を付与し、テーマやプラグインの更新時に自動で変わるため、手動管理は不要です。

画像最適化とメディア資産の管理についてはメディア資産の管理と画像圧縮をご覧ください。多言語サイトの静的リソースキャッシュではhreflangとCDNキャッシュの相互作用にも注意が必要です。詳しくはHreflang多言語対応。

五、キャッシュ無効化の戦略とよくある衝突

キャッシュを設定した後、最大の問題は「どうやってキャッシュを期限切れにすべきときに期限切れにするか」です。キャッシュが適切に無効化されないとユーザーは古いコンテンツを見てしまい、逆に無効化されやすすぎると性能のメリットが失われます。独立サイトでよくあるキャッシュ無効化のシナリオは4つあります:新規記事の公開、製品情報の更新、CSS/JSの変更、問い合わせフォームの送信です。それぞれのシナリオに異なる無効化戦略があります。

シナリオ 無効化範囲 無効化方法 注意点
新規ブログ記事の公開 トップページ+記事一覧+個別記事 WP Rocketが自動更新 カテゴリページとタグページも更新が必要
製品価格の更新 製品詳細ページ+一覧ページ 該当URLを手動でPurge 全体をPurgeしない、性能に影響する
CSS/JSの変更 該当する静的ファイル バージョン番号を変更すれば自動反映 max-ageを変更せず、ファイル名を変更
問い合わせフォームの送信 フォームページは絶対にキャッシュしない URL除外ルール 完了ページもキャッシュ不可
サイト全体のリニューアル すべてのキャッシュ ワンクリックでPurge All アクセスの少ない時間帯に実施
プラグイン/テーマの更新 すべてのキャッシュ 自動または手動でPurge 更新前にバックアップ

よくある衝突は2つあります。1つ目はキャッシュプラグインを2つインストールしている場合(例えばWP RocketとLiteSpeed Cacheを同時に導入)、両方のプラグインが.htaccessと出力バッファを変更し、ページが真っ白になったりスタイルが崩れたりします。ルールはサイト全体でキャッシュプラグインを1つだけにすることです。2つ目はページキャッシュとログイン状態の衝突です。ユーザーが管理画面にログインしても、フロントエンドは未ログインのキャッシュページのままなので、ナビゲーションバーが正しく表示されません。解決方法は、キャッシュプラグインがログインCookieを検知して自動的にキャッシュをスキップすることです。

キャッシュ戦略を公開した後は継続的に監視し、毎月キャッシュヒット率とLCPを確認します。キャッシュヒット率が突然下がった場合、通常は誰かがURL構造を変更したり、ランダムパラメータ付きのリンクを追加したことが原因です。監視とアラートのチェックリストはサイト監視とアラート、日常メンテナンス項目は公式サイト保守で毎月やる6つのことをご覧ください。バックアップと復元の演習もキャッシュ設定変更後に一度行ってください。詳しくはサイトのバックアップと復元演習。

FAQ よくある質問

WordPress独立サイトにキャッシュプラグインは必須ですか?
月間UV1万以上のサイトには必須です。ページキャッシュを設定するだけで、LCPを4秒以上から1秒以内に短縮でき、費用対効果が最も高い最適化です。WP RocketかLiteSpeed Cacheのどちらかを推奨します。二つ同時に導入しないでください。月間UV1000以下の小規模サイトなら、共有サーバーでもPHP自体が速いため、キャッシュプラグインなしでも動作しますが、入れても悪影響はありません。
問い合わせフォームのページはキャッシュできますか?
絶対にできません。フォームページと送信完了ページは必ずno-cacheに設定してください。キャッシュすると、次の訪問者がフォームを開いたときに前の訪問者の入力内容が表示され、送信データが混ざります。WP Rocketでは/inquiry*、/thank-you*、/wp-admin/*をNever Cache URL(s)リストに追加してください。同時に、キャッシュプラグインがログインcookieを検知して自動的にキャッシュをスキップすることを確認してください。
Redisオブジェクトキャッシュはいつ導入すべきですか?
二つの兆候が出てから導入してください。一つ目はMySQLスロークエリログで1秒を超えるクエリが1日100件以上あること、二つ目は管理画面で記事を保存してからフロントに反映されるまで数分かかることです。月間UV1万以下のB2Bサイトはデータベース負荷が小さく、ページキャッシュでリクエストの95%を処理できているため、Redisの効果は限定的です。月間UV5万以上、またはデータベースがボトルネックになったら、Redis+Redis Object Cacheプラグインを導入してください。
ブラウザキャッシュの期間はどれくらいが適切ですか?
画像とフォントは1年(max-age=31536000)、CSSとJSは7日(max-age=604800)、HTMLはキャッシュしません。内容を変更したときにユーザーが見られなくなるのを恐れて期間を短くしないでください。それでは訪問者のたびに毎回ダウンロードが発生し、ファーストビューがかえって遅くなります。更新問題の解決策は、ファイル名にバージョン番号やハッシュを付けることです。例えばstyle.css?v=2のように、内容が変わればURLも変わり、ブラウザが自動的に再ダウンロードします。
キャッシュで内容の変更が反映されない場合はどうすればいいですか?
二つのケースに分かれます。CDNキャッシュの場合は、CDNの管理画面で該当URLまたは全体を手動でPurgeしてください。サーバー側のページキャッシュの場合は、WP RocketとLiteSpeed Cacheは記事更新時に関連キャッシュを自動更新しますが、カスタムページやカテゴリページは手動Purgeが必要なことがあります。ブラウザキャッシュの場合はファイル名が変わっていないのが原因なので、次回公開時に静的ファイルへバージョン番号を付けてください。この問題を解決するためにmax-ageを短くするのは本末転倒です。
ページキャッシュとCDNキャッシュは競合しますか?
競合しません。両者は上流と下流の関係です。CDNキャッシュはエッジノードからユーザーまでの区間を、サーバー側ページキャッシュはオリジンサーバーの区間を担当します。正しい組み合わせは、HTMLはサーバー側ページキャッシュで生成し、CDNはHTMLに対してno-cacheの短いTTLを設定するかキャッシュせず、画像・CSS・JSのみをキャッシュするというものです。CDNがHTMLもキャッシュし、サーバー側ページキャッシュと重なると、ユーザーがフォームを送信した後に古い内容が表示される確率が大幅に高まります。

最後に

キャッシュの設定で初心者が陥りがちなのは両極端です。まったく設定せず毎回PHPとデータベースを動かしてファーストビューに4秒以上かけるか、逆に攻めすぎてフォームページまでキャッシュし、ユーザーに他人の送信内容を見せてしまうか。正解は「レイヤーごとに分ける」ことです。静的ページは積極的にキャッシュし、動的ページは絶対にキャッシュしない。静的アセットは長めにキャッシュしてバージョン番号を付けます。

やることは3つです。まずページキャッシュのプラグインを1つ入れ(WP RocketかLiteSpeed Cacheのどちらか)、フォームページとwp-adminを除外します。次にNginxのCache-Controlヘッダーを設定し、画像は1年、CSS/JSは7日、HTMLはキャッシュしないようにします。最後に月間UUが5万を超えたらRedisのオブジェクトキャッシュを導入します。ここまでやればLCPはほぼ1秒以内に収まり、キャッシュが内容を混線させる事故も起きません。

覚えておきたい一言。キャッシュは独立サイトのパフォーマンスを左右するてこです。ページキャッシュは時間を80%削減し、オブジェクトキャッシュはデータベースを助け、ブラウザキャッシュは再訪問を軽くします。3つの層がそれぞれ役割を担うので、フォームページだけはキャッシュに入れないでください。

环形山
环形山 クレーター専門家

公式アカウント · 環形山 Crater

環形山(Crater)は、WordPress独立サイト開発+Google SEO+GEO全域成長に特化した海外展開支援会社で、スマートハードウェア、産業機器、SaaS、新エネルギーなどの分野を手がけています。

プロダクト能力方法論ホワイトペーパー業界レポート
965件のシェア
78件のナレッジベース