Time to First Byteのキャッシュ時間を短縮する
キャッシュ時間は、Service Workerやブラウザキャッシュの検索時間を計測します。TTFBを短縮するためのキャッシュ戦略、Cache-Controlヘッダー、bfcache、Service Workerの最適化を解説します。
Time to First Byteのキャッシュ時間を短縮する
この記事は、Time to First Byte(TTFB)ガイドの一部です。キャッシュ時間(cache duration)はTTFBを構成する2番目の要素であり、ブラウザがローカルキャッシュやアクティブなService Workerを確認して、一致するレスポンスを探す時間を表します。キャッシュ時間が主要なボトルネックになることは稀ですが、Service Workerを使用しているサイトや、ブラウザキャッシュ戦略に大きく依存しているサイトでは、この仕組みを理解しておくことが重要です。
Time to First Byte(TTFB)は、以下の内訳に分解できます:
- 待機 + リダイレクト(またはwaiting duration)
- Worker + キャッシュ(またはcache duration)
- DNS(またはDNS duration)
- 接続(またはconnection duration)
- リクエスト(またはrequest duration)
Time to First Byteの改善をお探しですか? 本記事では、Time to First Byteの内訳のうちキャッシュ時間(cache duration)を解説します。Time to First Byteの基本を理解したい、または問題を改善したいものの、cache durationの意味が分からない場合は、読み進める前にTime to First ByteとはおよびTime to First Byte問題の特定と改善をご覧ください。
注:通常、Time to First Byteにおいてキャッシュ時間がボトルネックになることはありません。a)Service Workerを使用している、または b)筆者のようなページスピード愛好家であれば、このまま読み進めてください。
通常、Time to First Byteのキャッシュ時間はボトルネックにならず、10〜20ms程度で完了します。Service Workerを使用している場合でも、60ms未満であれば許容範囲です。
Table of Contents!
Service WorkerはTime to First Byteにどう影響するか
Service WorkerはTime to First Byte(TTFB)にプラスとマイナスの両面の影響を与えます。ただし、これはService Workerを使用しているWebサイトに限られます。
Service WorkerがTTFBに与える影響は以下のとおりです:
Service Workerの起動時間によるTTFBの遅延:workerStartの値は、リクエストされたリソースに対してService Workerが存在する場合の起動時間を表します。この起動時間はTTFBの計算に含まれます。Service Workerを新規起動したり、停止状態から復帰させたりする必要がある場合、初期応答時間に遅延が生じ、TTFBが悪化します。
キャッシュによるTTFBの高速化:stale-while-revalidateのようなキャッシュ戦略を使用すると、利用可能なコンテンツをService Workerがキャッシュから直接返します。ブラウザがサーバーの応答を待つ必要がないため、頻繁にアクセスされるリソースのTTFBはほぼ瞬時になります。この戦略は、最新情報が必須となる動的生成コンテンツよりも、更新頻度の低いコンテンツに最適です。
App ShellモデルによるTTFBの高速化:クライアントサイドでレンダリングするアプリケーションでは、App Shellモデル(Service Workerが基本となるページ構造をキャッシュから返し、コンテンツは後から動的に読み込む方式)を採用することで、ベース構造のTTFBをほぼゼロまで削減できます。
未最適化のコードによるTTFBの遅延:Service Workerの処理が複雑で非効率な場合、キャッシュの検索処理が遅くなり、結果としてTTFBも悪化します。
Service Workerはページスピードに悪影響を与えますか? いいえ、(通常は)まったく悪影響はありません。起動時間によって初回TTFBが増加することはあっても、ネットワークリクエストのインターセプト、キャッシュ管理、オフライン対応などの機能により、長期的には大幅なパフォーマンス向上が得られます。特にサイトの再訪問者に対して絶大な効果を発揮します。
Service Workerのキャッシュ戦略
Service Workerで使用するキャッシュ戦略によって、速度と鮮度のバランスが決まります。戦略ごとに、TTFBのキャッシュ時間に与える影響は異なります:
Cache-First(キャッシュ優先、ネットワークへフォールバック)
Service Workerはまずキャッシュを確認します。キャッシュされたレスポンスが存在する場合は、即座にそれを返します。存在しない場合のみネットワークへリクエストを送信します。この戦略はキャッシュされたリソースに対して最速のTTFBを実現しますが、古いコンテンツを返してしまうリスクがあります。
最適な用途:静的アセット、画像、フォント、更新頻度の低いコンテンツ。
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((cachedResponse) => {
if (cachedResponse) {
return cachedResponse;
}
return fetch(event.request).then((networkResponse) => {
const cache = await caches.open('v1');
cache.put(event.request, networkResponse.clone());
return networkResponse;
});
})
);
}); Network-First(ネットワーク優先、キャッシュへフォールバック)
Service Workerは常にネットワークを優先して通信を試みます。ネットワークリクエストが失敗した場合(オフライン時など)にのみ、キャッシュされたバージョンを提供します。この戦略はネットワーク接続時に最新のコンテンツを保証しますが、オンラインユーザーのTTFBを短縮することはできません。
最適な用途:APIレスポンス、頻繁に更新されるコンテンツ、鮮度が極めて重要なページ。
Stale-While-Revalidate
Service Workerはキャッシュされたバージョンを即座に返し(高速なTTFB)、同時にバックグラウンドでネットワークから最新版を取得します。取得した最新版は、次回アクセス時のためにキャッシュへ保存されます。多くの場合、速度と鮮度のバランスが最も優れた戦略です。
最適な用途:定期的に更新されるものの、厳密なリアルタイム性を必要としないコンテンツ(ニュース記事、ブログ記事、商品一覧など)。
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.open('v1').then((cache) => {
return cache.match(event.request).then((cachedResponse) => {
const fetchPromise = fetch(event.request).then((networkResponse) => {
cache.put(event.request, networkResponse.clone());
return networkResponse;
});
return cachedResponse || fetchPromise;
});
})
);
}); Cache-Controlヘッダーの設定
TTFBのキャッシュ時間は、Service Workerやブラウザキャッシュの検索に費やされた時間を計測するものですが、そもそもブラウザがサーバーと通信する必要があるかどうかは適切なCache-Controlヘッダーによって決まります。正しいキャッシュヘッダーを設定すれば、再訪問者に対してTTFB全体を実質的にバイパスできます。
リソースタイプ別の推奨Cache-Control設定は以下のとおりです:
# HTMLページ(常に再検証) Cache-Control: no-cache # コンテンツハッシュ付きの静的アセット(無期限にキャッシュ) Cache-Control: public, max-age=31536000, immutable # コンテンツハッシュのない画像(1週間キャッシュ) Cache-Control: public, max-age=604800 # APIレスポンス(キャッシュ不可) Cache-Control: no-store
主要ディレクティブの解説:
- no-cache:ブラウザはキャッシュを使用する前に、必ずサーバーへ再検証を行う必要があります。「キャッシュしない」という意味ではなく、「必ず事前に確認する」という意味です。
- no-store:ブラウザはレスポンスを一切キャッシュしてはなりません。機密情報や極めて動的なコンテンツに使用します。
- max-age:再検証なしでキャッシュからレスポンスを返せる時間(秒数)です。
- immutable:リソースが変更されないことをブラウザに伝えます。静的アセットでは、ファイル名にコンテンツハッシュを含める構成(例:
style.a1b2c3.css)と組み合わせて使用します。 - public:共有キャッシュ(CDNやプロキシ)によるレスポンスのキャッシュを許可します。ユーザー固有のコンテンツにはprivateを使用してください。
CloudflareなどのCDNを使用している場合は、エッジキャッシュルールを設定することも可能です。詳細な手順については、パフォーマンス向上のためのCloudflare設定ガイドを参照してください。
Back/Forward Cache(bfcache)
Back/Forward Cache(bfcache)は、ユーザーがページから離脱した際に、そのページの完全なスナップショットをメモリに保存するブラウザの最適化機能です。ユーザーが「戻る」または「進む」ボタンを押すと、メモリから瞬時にページが復元され、TTFB(および他のすべての読み込み指標)が実質ゼロになります。
ネットワークリクエストが一切発生しないため、bfcacheから読み込まれたページはRUMデータ上で0ミリ秒(0ms)のTTFBとして記録されます。ブラウザはメモリ上のスナップショットからページを復元するだけです。
ページをbfcacheに対応させるための要件は以下のとおりです:
unloadイベントリスナーは使用しないでください(代わりにpagehideを使用します)。- HTMLドキュメントに
Cache-Control: no-storeを設定しないでください。 - ページが非表示になったら、開いているIndexedDB接続を閉じてください。
- アクティブなWebSocketやWebRTC接続を維持したままにしないでください(
pagehideイベント内で切断します)。
Chrome DevToolsの「Application」タブにある「Back/Forward Cache」セクションで、bfcacheの適合性をテストできます。ページがbfcacheの対象外となった場合、Chromeがその理由を一覧で表示してくれます。
戻る・進む操作の割合が高いサイト(ECサイトのカテゴリ一覧や商品ページ、検索結果ページなど)では、bfcacheによって遷移の大部分で体感TTFBを劇的に改善できます。
Time to First Byteのキャッシュ時間を計測する方法
Time to First Byteのキャッシュ時間は、以下のスニペットで計測できます:
new PerformanceObserver((entryList) => {
const [navigationEntry] = entryList.getEntriesByType('navigation');
// 関連するタイムスタンプを取得
const activationStart = navigationEntry.activationStart || 0;
const waitEnd = Math.max(
(navigationEntry.workerStart || navigationEntry.fetchStart) -
activationStart,
0,
);
const dnsStart = Math.max(
navigationEntry.domainLookupStart - activationStart,
0,
);
// キャッシュ時間を計算
const cacheDuration = dnsStart - waitEnd;
// 結果をログ出力
console.log('%cTTFB cacheDuration', 'color: blue; font-weight: bold;');
console.log(cacheDuration);
}).observe({
type: 'navigation',
buffered: true
}); キャッシュ時間の増大によるTTFBの問題を特定する方法
キャッシュ時間によって実際のユーザーが受けている影響を把握するには、CoreDashのようなRUMツールが必要です。Real User Monitoringを利用することで、Core Web Vitalsを高い精度で詳細に追跡できます。
CoreDashでは、「Time to First Byte」へ移動して内訳の詳細を確認するだけです。TTFBを構成するすべての要素への分解が表示され、75パーセンタイルにおいてcacheDurationにどれだけの時間がかかっているかを正確に把握できます。

Service Workerによるキャッシュ時間の影響を最小限に抑える方法
Service Worker使用時にTTFBを最適化するには:
- Service Workerスクリプトの処理を最小限に抑え、起動時間を短縮してください。
- Service Worker内で効率的なキャッシュ戦略を実装してください(ナビゲーションリクエストにはstale-while-revalidateを優先します)。
- Service Workerのインストール時に、クリティカルリソースの事前キャッシュを検討してください。
- Service WorkerがサイトのTTFBに与える影響を定期的に監視・分析してください。
navigation preloadを使用して、Service Workerの起動と並行してネットワークリクエストを実行してください。これにより、Service Workerの起動時間がTTFBに上乗せされるのを防ぐことができます。
Service Workerでnavigation preloadを有効にするコードは以下のとおりです:
self.addEventListener('activate', (event) => {
event.waitUntil(
(async () => {
if (self.registration.navigationPreload) {
await self.registration.navigationPreload.enable();
}
})()
);
}); 正しく実装すれば、Service Workerは再訪問者の読み込みを高速化します。実装を誤れば、ページを開くたびに起動コストを支払うことになります。
関連ガイド:最適化の解説
関連ガイド:
- 103 Early Hints:サーバーの応答準備が整う前に重要リソースの読み込みを開始し、キャッシュ戦略を補完します。
- パフォーマンス向上のためのCloudflare設定ガイド:CDNエッジキャッシュ、キャッシュルール、ページルールを設定し、エッジレベルでキャッシュ戦略を最適化します。
TTFBの内訳:完全ガイド
キャッシュ時間は、TTFBを構成する5つの内訳の1つです。全体像を把握するために、他の内訳についても確認してください:
- TTFB問題の特定と改善:すべてのTTFB最適化における診断の出発点。
- Waiting Duration(待機時間):リダイレクト、ブラウザのキューイング、HSTSの最適化。
- DNS Duration(DNS解決時間):DNSプロバイダの選定、TTL設定、dns-prefetch。
- Connection Duration(接続時間):TCPハンドシェイク、TLS最適化、HTTP/3、preconnect。
- Request Duration(リクエスト処理時間):サーバー処理時間、データベースクエリ、バックエンドの最適化。
何が本当に遅いのか、見つけ出します。
フィールドデータでCritical Rendering Pathをマッピング。Lighthouseレポートではなく、優先順位付きの修正リストをお渡しします。
監査を依頼する