Lighthouseの「Use efficient cache lifetimes」: 修正方法
Lighthouse 13は、訪問者のブラウザで再利用できる期間が30日未満のファイルをすべてリストアップします。次のページや再訪問時にどれだけの遅延が生じるのか、そしてサーバーやプラットフォームごとの修正方法を解説します。
「Use efficient cache lifetimes」はLighthouseスコアを下げますか?
Lighthouseは、「Use efficient cache lifetimes」インサイト(「キャッシュの有効期間を長くすると、ページへの再訪問をスピードアップできます」)で、キャッシュ有効期間が30日未満の静的ファイルをすべて指摘します。この項目は、Lighthouse 13より前は「Serve static assets with an efficient cache policy」、古いPageSpeed Insightsでは「Leverage browser caching」と呼ばれていました。これはスコアの算出対象外であり、スコアを動かすこともありません。Lighthouseは常にキャッシュが空の状態で読み込むからです。サーバー、CDN、またはキャッシュプラグインで有効期間を長く設定してください。
Table of Contents!
Lighthouseの「Use efficient cache lifetimes」は何を意味しますか?
Lighthouseの説明には、「キャッシュの有効期間を長くすると、ページへの再訪問をスピードアップできます。キャッシュについて詳しく見る」と記載されています。
Lighthouseは、キャッシュ有効期間が30日未満のスクリプト、スタイルシート、フォント、画像、動画、音声ファイル、および有効期間が設定されていないファイルをすべてリストアップします。有効期間とは、ファイルのCache-Controlヘッダーに含まれるmax-age、またはExpiresの日時までの時間です。別ドメインのファイルも、自サイトのファイルと同様にリストアップされます。
Lighthouseは、max-ageの値に関係なく、Cache-Controlにno-cache、no-store、private、must-revalidateのいずれかが指定されているファイルを除外します。キャッシュしない方針であると判断するためです。また、max-age=0のファイルも除外され、HTMLページ自体がリストアップされることはありません。
Google Tag Manager、GA4、Meta Pixelを読み込むページ。Googleのタグはprivateとして送信されるため、この3つのうちMeta Pixelのみがリストアップされています。Lighthouse 13.5.0、PageSpeed Insightsと同様のシミュレートされたスロットリング。
ブラウザキャッシュはどのように機能しますか?
ブラウザはファイルを有効期間とともに保存します。次のページを開いたときや再訪問したとき、ブラウザはまず保存されているコピーを確認します。
- 有効期間内の場合: ブラウザは保存したコピーを使用し、リクエストを一切送信しません。サーバーの応答を待たずに済むのはこのケースだけです。
- 有効期間が切れている場合: ブラウザはファイル取得時に受け取ったETagまたはLast-Modifiedの日時を使って、ファイルが変更されたかをサーバーに問い合わせます。変更されていない場合、サーバーはファイル本体を含めずに304 Not Modifiedを返します。ダウンロードは不要になりますが、ファイルごとにサーバーへの往復通信が発生します。また、すべてのスタイルシートに対してサーバーから応答があるまで、ページは描画できません。
- 有効期間が切れ、ETagもLast-Modifiedもない場合: ブラウザはファイルを再度ダウンロードします。
| ファイルのヘッダー | Chromeでの次回表示 |
|---|---|
max-age=31536000 | 1年間キャッシュから読み込み |
max-age=14400 | 4時間はキャッシュから読み込み、その後は304 |
no-cache | 毎回304応答 |
no-store | 毎回ダウンロード |
Chromeでの通常のリロードでは、有効期間内のファイルを再確認しません。FirefoxとSafariは、Cache-Controlにimmutableが指定されていない限り再確認します。
Chrome 86以降、サイトごとに個別のキャッシュが使われます。訪問者が他のサイトでダウンロード済みのフォントやjQueryファイルであっても、同じURLであるかどうかにかかわらず自サイトで再ダウンロードされます。したがって、サードパーティの有効期間が効くのは自サイト内(次のページや再訪問)に限られます。「誰もがすでにキャッシュしている」という理由は、Googleからフォントを読み込む根拠にはもうなりません(セルフホストしてください)。
ブラウザキャッシュとCDNのキャッシュは別物です。WordPressのキャッシュプラグインによるページキャッシュも同様です。CDNやページキャッシュはコピーを保持することで、サーバーが訪問者ごとにデータを生成・送信する手間を省き、TTFBを短縮します(キャッシュ期間)。一方、ブラウザキャッシュは特定の訪問者に対してリクエストそのものを不要にします。このインサイトが対象としているのはブラウザキャッシュのみです。そのため、すでにページキャッシュを有効にしていても、この指摘は解消されません。
キャッシュの有効期間を延ばすとLCPやFCPは改善しますか?
初回訪問時のFCPとLCP
変化はありません。Slow 4G環境のスマートフォンでWooCommerceの商品ページをテストしたところ、ヘッダー未設定から1年間の設定まで、初回描画は2.13秒、LCPは2.26秒でまったく同じでした。初回訪問時にはキャッシュがまだ存在しないからです。LighthouseやPageSpeed Insightsは、常にこの初回訪問の状態でページを読み込みます。そのため、Lighthouseでこのインサイトが赤字で表示され、FCPやLCPの短縮見込みが示されていたとしても、ここを改善してスコアやメトリクスが動くことはありません。

有効期間なし

Cloudflareの4時間

1年間

no-store
ファイルに4種類のキャッシュヘッダーを設定した同一の商品ページ。Lighthouse 13.5.0、モバイル、シミュレートされたスロットリング。
2ページ目および再訪問時のFCPとLCP
初回訪問時の2.13秒に対する初回描画(FCP)の秒数:
| キャッシュヘッダー | 次のページ | 1週間後 |
|---|---|---|
| 1年間 | 0.79 | 0.78 |
| 4時間 | 0.80 | 1.94 |
no-cache | 1.93 | 1.94 |
no-store | 2.11 | 2.12 |
- 効果が得られるのは、ファイルが有効期間内にある場合だけです。 1年間の有効期間を設定したページは、1週間後でも0.78秒で描画され、初回訪問時の約3分の1の時間で済みました。残る待ち時間はHTMLの取得待ちだけです。再訪問時のLCP(商品写真)は、no-store(2.24秒)を除くすべてのヘッダーで初回描画とまったく同じタイミングで発生しました。
- 304応答の場合、再訪問時の速度は初回訪問とほぼ変わりませんでした。 有効期間を設定しなかった場合、1週間後にはショップ自体の全13ファイルが304応答となりました。ネットワーク転送量は404 kBから1 kBに減ったものの、初回描画は1.94秒かかり、すべてを再ダウンロードした場合よりわずか0.19秒早くなっただけでした。
- 短い有効期間は同一セッションしかカバーできず、再訪問前には切れてしまいます。 Cloudflareの4時間設定では次のページはキャッシュから読み込まれましたが、1週間後には304応答が必要になりました。両方をカバーできたのは1年間の設定だけでした。
- no-cacheとno-storeはすべてのページを遅くします。 次のページの表示時間が0.79秒から1.93秒、2.11秒へと遅くなりました。

有効期間なし(1週間後)

有効期間1年(1週間後)
スロットリングされたスマートフォンでの、0.8秒時点の両ページ。
実際のSlow 4G回線ではどの表示もさらに時間がかかりますが、ヘッダーごとの順位は変わりません。Fast 4G回線では、有効期間1年の再訪問時の描画は0.52秒でした。一方、304応答を伴う再訪問は0.85秒かかり、その回線での初回訪問と同じだけの時間を要しました。
「Use efficient cache lifetimes」はどんなときに無視できますか?
Lighthouseで自サイトのファイルがリストアップされており、訪問者が数日後や数週間後に再訪するサイトであれば、修正してください。サーバー側の設定ルール1つで対応でき、ページ読み込み時の負荷も一切ありません。逆に、次のような場合は放置して構いません:
- リストアップされたファイルがすべて外部ドメインにあるか、利用しているプラットフォームから配信されている場合(後述)。Shopifyでは常にこれに該当します。
- CSSやJavaScriptが同一ファイル名のまま変更され、バージョンを付与できない場合。この場合は有効期間を短く設定するのが正しい選択であり、Lighthouseにリストアップされ続けるのもやむを得ません。
- 広告キャンペーンのランディングページのように、ほとんどすべての訪問者が1ページだけ見て二度と戻ってこない場合。キャッシュを利用する人がいないためです。
field dataでキャッシュ有効期間の効果をどのように測定しますか?
field dataには、初回訪問、次のページへの遷移、再訪問が混在しています。CoreDashでモバイルのLCPをRepeat Visitor別にグループ化し、TTFBも同様にグループ化してください:
デモサイト(acme-pagespeed.com)でのCoreDash: モバイル、p75。
再訪問者のLCPは0.65秒、新規訪問者は1.74秒です。この差のすべてがキャッシュによるものではありません。再訪問者は最初の1バイトも0.87秒に対して323 msと高速です。ブラウザがすでにサーバーとの接続を確立していることが多いためです。そこで、LCPからTTFBを差し引いてみます。最初の1バイトを受信した後、再訪問者が待つ時間は0.33秒、新規訪問者は0.87秒です。この差である0.54秒がおおよそキャッシュによって削減された時間であり、全体の差の約半分を占めます。残りの半分は最初の1バイトの高速化によるものです。
訪問中の「次のページ」だけを切り分けて確認するには、Navigation Originでグループ化します。same originは、訪問者が自サイト内の別のページから遷移してきたことを意味します。CrUXはこれらすべての訪問をページごと、またはサイトごとの単一のp75値にまとめてしまうため、このような内訳を確認することはできません。
DevToolsでファイルのキャッシュヘッダーをどのように確認しますか?
Networkパネルを開き、Disable cacheのチェックが外れていることを確認します(オンにすべきタイミングについてはDevToolsのネットワーク設定を参照してください)。ページを読み込んだ後、2ページ目へ遷移します。各ファイルの状態はStatus列とSize列で確認できます:
- Size列に(disk cache) または (memory cache): キャッシュから取得され、リクエストは発生しません。
- Status列に304: ブラウザがサーバーに問い合わせを行い、ファイル本体は返送されていません。
- サイズが表示されている200: 再度ダウンロードされました。

有効期間なし: すべてのファイルで304

1年間: ディスクキャッシュから取得
Networkパネルでの1週間後の再訪問(Slow 4G)。
ファイルをクリックしてHeadersタブを開きます。Response Headers内のcache-controlとexpiresはサーバーが送信した有効期間を示し、etagとlast-modifiedはブラウザが再確認に使用できる値を示します。コマンドラインから確認する場合は次のようにします:
curl -sI https://www.example.com/wp-content/themes/acme/style.css | grep -iE "cache-control|expires|etag|last-modified"
Performanceパネルにも同じインサイトがあります。リロードを記録し、Insightsサイドバーで「Use efficient cache lifetimes」を開きます。対象ファイルがリストアップされ、Networkトラック上で枠線表示されます。有効期間のないファイルはPerformanceパネルでは0 ms、LighthouseではNoneと表示されます。判定はヘッダーに基づいて行われるため、キャッシュから読み込まれたファイルであっても、有効期間が短ければリストアップされます。
「Use efficient cache lifetimes」はどのように修正しますか?
Lighthouseがリストアップした各ファイルのドメインを見れば、誰がヘッダーを設定しているかがわかります。自ドメインの場合は自前のサーバー、ホスティング事業者、またはCDNです。それ以外のドメインの場合は、そのファイルのベンダーです。
nginx、Apache、IIS、Caddy
自前のサーバーでは、ファイルタイプごとにルールを1つ記述して有効期間を設定します。以下のルールはimmutableを付与して1年間に設定するものです。画像ファイル、およびURLにバージョンが含まれているCSS、JavaScript、フォントに使用してください(ファイルごとの推奨有効期間)。nginxの設定例:
location ~* \.(?:css|js|mjs|woff2|svg|png|jpe?g|gif|webp|avif|ico)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
}
ディスクからこれらのファイルを配信しているserverブロックに記述します。設定ファイル内にこれらの拡張子に一致するlocationブロックがすでにある場合は、そちらにadd_headerの行を追加してください。nginxは最初に一致した正規表現locationのみを適用するためです。また、既存のexpires行があれば削除してください。そうしないとCache-Controlヘッダーが二重に出力されます。なお、location内でadd_headerを使用すると、serverブロックで定義されたすべてのadd_headerがそのファイルに対して無効になります。そのため、serverブロックにあるadd_header行(HSTSヘッダーなど)もこのlocation内にコピーしてください。
Apacheの場合は、.htaccessに以下を記述します:
<IfModule mod_headers.c>
<FilesMatch "\.(css|js|mjs|woff2|svg|png|jpe?g|gif|webp|avif|ico)$">
Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>
</IfModule>
FilesMatchはファイル名で判定されるため、PHPが動的に生成するページには独自ヘッダーがそのまま維持されます。設定後もLighthouseがファイルをリストアップし続ける場合、サーバーでルールが適用されていません。mod_headersが無効になっているか、ホスティング事業者が.htaccessを無視する別のサーバーからこれらのファイルを配信しています。ホスティング事業者に問い合わせてください。
IISはデフォルトでは有効期間を一切送信しません。静的ファイルが置かれているフォルダーを対象として、web.configでヘッダーを設定します:
<configuration>
<location path="assets">
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Cache-Control" value="public, max-age=31536000, immutable" />
</customHeaders>
</httpProtocol>
</system.webServer>
</location>
</configuration>
Caddyの場合:
@static path *.css *.js *.mjs *.woff2 *.svg *.png *.jpg *.jpeg *.gif *.webp *.avif *.ico header @static Cache-Control "public, max-age=31536000, immutable"
WordPress
WordPress本体は、/wp-content/や/wp-includes/配下のファイルに対して有効期間を一切送信しません。設定を決めるのはホスティング事業者、サーバー、またはプラグインです。Kinsta、WP Engine、WordPress VIP、WordPress.comは、すでに1年の有効期間を送信しています。これらを利用している場合、Lighthouseにリストアップされるファイルは通常、外部ドメインのものです(後述)。この警告がよく発生するのは、有効期間を設定していない共有Apacheサーバー、自身で運用するnginxサーバー、そしてCloudflareの初期設定の背後にある環境です。主要なキャッシュプラグインの挙動は以下のとおりです:
- WP Rocketは、ApacheおよびLiteSpeedサーバー上の.htaccessにmod_expiresのルールを書き込みます。CSSとJavaScriptには1年、画像とフォントには4か月を設定します。nginx環境では何もしないため、前述のnginxルールを手動で追加するか、ホスティング事業者に依頼してください。
- LiteSpeed Cacheは、デフォルトでBrowser Cacheが無効になっています。LiteSpeed Cache、Cache、Browserの項目で有効化してください。これで1年の有効期間が送信されます。
- W3 Total Cacheは、デフォルトでBrowser Cacheが有効になっています。nginx環境ではnginx.confが出力されるため、サーバー設定に自身でインクルードする必要があります。
WordPressは、読み込むすべてのスタイルシートとスクリプトに?ver=を付与します。デフォルトではWordPressのバージョン、またはテーマやプラグインが渡すバージョンです。これはアップデート時にしか変わりません。バージョンを変更せずにCSSを直接編集するとURLが変わらないため、再訪問者にはキャッシュの有効期間が切れるまで古いCSSが表示され続けます。編集のたびにURLが変わるよう、ファイルの更新日時をバージョンとして読み込んでください:
add_action( 'wp_enqueue_scripts', function () {
$file = '/css/custom.css';
wp_enqueue_style( 'acme-custom', get_stylesheet_directory_uri() . $file, array(), filemtime( get_stylesheet_directory() . $file ) );
} );
アップロードされたファイルにはバージョンが付与されません。ファイルを再アップロードすると、WordPressが自動的に新しいファイル名(photo-1.jpgなど)を割り振るため、プラグインが同名でメディアを上書きしない限り、1年間に設定しても安全です。
CloudflareなどのCDN
CloudflareのBrowser Cache TTLは、すべてのプランでデフォルト4時間に設定されています。オリジンサーバーが有効期間を送信していないか、より短い値を送信している場合、Cloudflareは4時間を送信し、Lighthouseには「4h」としてリストアップされます。サーバー側で有効期間を設定したうえで、Browser Cache TTL(Caching、Configuration)をRespect Existing Headersに変更してください。または、静的ファイルの拡張子を対象にしたCache Ruleを作成し、ブラウザのTTLを設定します。ゾーン全体の設定を一括で1年に引き上げるのは避けてください。バージョン管理されていないCSSを含め、Cloudflareが短いヘッダーで配信しているすべてのファイルに適用されてしまうためです。その他の設定については、Core Web Vitalsに向けたCloudflareの設定を参照してください。
CloudFrontとFastlyは、サーバーのヘッダーをそのままブラウザにパススルーします。これらのTTL設定は、CDNがキャッシュコピーを保持する期間を変更するだけです。Bunnyでは、Override Browser Cache Timeを指定したEdge Ruleを使用することで、拡張子ごとに有効期間を設定できます。
Shopify
Shopifyはすでに、テーマファイル、アップロードされたファイル、商品画像に1年間の有効期間(public, max-age=31557600)を設定しており、Shopify側のヘッダーを変更することはできません。Shopify環境でLighthouseにリストアップされるのは、Shopify本体や導入アプリに起因するものです。具体的には、動的チェックアウトボタン(portable-wallets、自ドメインの/cdn/shopifycloud/配下で5分)、shop-jsローダー(30分)、web pixels manager(60秒)などです。修正すべき点はありません。この警告は無視してください。
Next.jsおよびその他のビルドツール
Next.jsは、/_next/static/配下のすべてにimmutable付きで1年の有効期間を設定しており、これをオーバーライドすることはできません。public/配下のファイルにはmax-age=0(Vercelではmust-revalidate付き)が設定され、/_next/imageはmust-revalidateを送信するため、このインサイトではどちらもスキップされます。Next.jsサイトでLighthouseにリストアップされるファイルは、サードパーティ製であるか、前段のプロキシやCDNがヘッダーを書き換えていることが原因です。内容に応じてファイル名が変わるpublic/配下のファイルについては、next.config.jsで1年を設定してください:
module.exports = {
async headers() {
return [{
source: '/fonts/:path*',
headers: [{ key: 'Cache-Control', value: 'public, max-age=31536000, immutable' }],
}]
},
}
他の技術スタックを利用している場合は、バンドラーでファイル名にハッシュを付与し(app.3f9a1c.jsなど)、変更のたびに新しいURLが生成されるようにしてください。そのうえで、それらのファイルに1年の有効期間を設定して送信します。
変更できないサードパーティおよびプラットフォームのファイル
ベンダーはスクリプトを一斉にアップデートできるよう有効期間を短く設定しており、利用者がそのヘッダーを変更することはできません。Lighthouseでは、ベンダーが設定した以下の有効期間とともにリストアップされることがよくあります:
- Meta Pixel (fbevents.js): 20分
- Hotjar: 60秒
- Google Analyticsの旧analytics.js: 2時間
- Google Maps (maps/api/js): 30分
- Stripe (js.stripe.com/v3): 2分
- 自ドメイン上のCloudflare独自スクリプト(email-decode.min.js、Rocket Loader): 2日
これらは放置してください。独自のヘッダーを付与したいからといって、fbevents.jsやgtag.jsなどのローダーをセルフホストしてはいけません。ベンダーによるアップデートを受け取れなくなります。対応できる手段は以下のとおりです:
- URLに厳密なバージョン番号を含めて公開CDNからライブラリを読み込みます。jsDelivrでは、swiper@11には7日間、バージョンを固定したURLには1年間の有効期間が設定されます。あるいは、バージョン固定したファイルを自前のヘッダーとともにセルフホストします。
- Email Address Obfuscationなど、不要なCloudflare機能をオフにします。
- 使われていないタグを削除します。
どのファイルにどのキャッシュ有効期間を設定すべきか
- URLにバージョンやハッシュが含まれるCSS、JavaScript、フォント: immutable付きで1年間:
Cache-Control: public, max-age=31536000, immutable。新しいバージョンは新しいURLになるため、ブラウザは即座に取得します。 - 画像: 同様のヘッダーを設定します。画像を変更した際は新しいファイル名を付ければ、古い画像が表示されることはありません。
- バージョンのないCSS、JavaScript、フォント: まずバージョンを追加してください(前述のWordPressやビルドツールの手順を参照)。対応できるまでは有効期間を短く保ち、Lighthouseにリストアップされるのを受け入れます。同じ名前のまま更新されるスタイルシートに長い有効期間を設定すると、再訪問者に新しいHTMLと古いCSSが組み合わされて表示されてしまいます。
- HTML:
Cache-Control: no-cacheまたは短いmax-age。長い有効期間を設定すると、訪問者に古いページが保持され続けてしまうためです。 - 警告を消すためだけに静的ファイルへno-store、no-cache、must-revalidateを設定することは絶対に避けてください。 ページを開くたびに全ファイルを再確認または再ダウンロードすることになります(前述の2ページ目の検証結果を参照)。
設定したキャッシュ有効期間が機能しているかをどのように確認しますか?
まずは前述のcurlまたはHeadersタブを使い、ファイルのヘッダーを確認してください。新しいmax-ageが表示されているはずです。次にモバイルでLighthouseを実行します。ファイルはリストアップされなくなり、前述の4つのレポートのとおり、スコアやメトリクスは以前と変わりません。商品ページのテストでは、実行ごとにスコアが96や80になりましたが、その違いはヘッダーとは一切関係ありませんでした。したがって、修正後にスコアが変わったとしても、キャッシュの効果を示すものではありません。
効果が最初に表れるのはCoreDashの再訪問者データであり、数日以内に新しい有効期間でファイルを取得するユーザーが現れます。CrUXやSearch Consoleは過去28日間の集計を表示するため、修正から2週間経っても、表示データの半分は修正前の古いサイトのままです。
リストアップされたファイルのうちどれを修正すべきかわからない場合や、再訪問者がすでに持っているファイルを無駄に待たされていないか確認したい場合は、Core Web Vitals パワーアワーをご利用ください。ヘッダーとfield dataを一緒に精査し、通話が終わるまでにサーバー向けの設定ルールをお渡しします。