キャッシュの挙動が期待と違うときは、まずレスポンスヘッダー Cf-Cache-Status を確認します。すべてのレスポンスに付き、その値でそのリクエストに何が起きたかが分かります。
同じ URL に 2 回リクエストを送り、ヘッダーを比較します。
curl -I https://my-worker.example.workers.dev/api/users/42
curl -I https://my-worker.example.workers.dev/api/users/42ステータス値を、次のシナリオと照合します。
Cf-Cache-Status がありません。Wrangler のバージョンが 4.69.0 以上であることと、wrangler.toml または wrangler.jsonc でその Worker に cache.enabled = true があることを確認します。
Cf-Cache-Status が毎回 MISS、または DYNAMIC、または BYPASS です。キャッシュに何も保存されていないか、バイパスルールが発動しています。
Cache-Control ヘッダーを確認します。 レスポンスには、キャッシュ可能にするディレクティブが必要です。
public, max-age=N— Cloudflare とブラウザーにN秒間キャッシュされます。
Cache-Control: private または no-store のレスポンスは保存されず、Cf-Cache-Status は BYPASS です。
Cache-Control: no-cache のレスポンスは 保存されます が、Cloudflare は以降のすべてのリクエストを stale とみなし、配信前にご自身の Worker に問い合わせます。正確な Cf-Cache-Status は、stale-while-revalidate も設定されているかによって変わります。
Cache-Control: no-cacheだけの場合、以降のすべてのリクエストでインライン再検証が走ります。Worker が304 Not Modifiedを返すとCf-Cache-StatusはREVALIDATED(本文はキャッシュから配信)、新しい200を返すとEXPIRED(本文は置き換え)です。Cache-Control: no-cache, stale-while-revalidate=Nの場合、キャッシュされた本文はすぐ配信され、Worker はバックグラウンドで実行されます。SWR ウィンドウ中のCf-Cache-StatusはUPDATINGです。
長期間ヒットさせたい場合は、代わりに max-age を使います。no-cache はバイパスではありません を参照してください。
レスポンスに Cache-Control ヘッダーが ない 場合、挙動はステータスコードに依存します。Workers Caching は RFC 9111 のヒューリスティック鮮度 ↗ を適用し、デフォルトでキャッシュ可能なステータスコードをヒューリスティック TTL でキャッシュします。たとえば 200 は 2 時間、404 は 3 分です。デフォルト TTL の一覧は、設定リファレンスの Cache-Control ヘッダーがないレスポンスもキャッシュされる を参照してください。これらのデフォルトを使いたくない場合は、レスポンスに Cache-Control を明示します。
リクエストメソッドを確認します。 キャッシュされるのは GET と HEAD だけです。それ以外は BYPASS です。同じ URL の GET と HEAD は同じキャッシュエントリを共有します。どちらのメソッドからキャッシュを埋めるかは キャッシュキー を参照してください。
自動バイパス条件を確認します。 Cloudflare は次の場合にキャッシュをバイパスします。
- レスポンスに
Set-Cookieヘッダーがある。 - リクエストに
Authorizationヘッダーがある。ただしレスポンスが明示的にCache-Control: public、must-revalidate、またはs-maxageを設定している場合を除く。
Worker が無条件に Set-Cookie を付ける(例: すべてのレスポンスにセッション Cookie)と、レスポンスはキャッシュされません。キャッシュ可能なレスポンスから Cookie を外すか、Cookie 設定とキャッシュ可能なレスポンスを別ルートに分けます。
ステータスコードを確認します。 Workers Caching は RFC 9111 ↗ に従います。デフォルトでキャッシュできないステータスコード(例: 401、403、500)のレスポンスは、キャッシュ可能なディレクティブを明示しない限り保存されません。
一部のステータスコードは、明示的な Cache-Control があってもキャッシュされません。
520–526は Cloudflare のフェイルセーフレスポンスとして扱われ、保存されません。- Worker が返す
206 Partial Contentは保存されません。Workers Caching はRangeリクエストを、Worker から全文を取得してキャッシュエントリから切り出すことで自ら処理します。Worker が独自の206を返すとそのレスポンスはキャッシュ不可になります。代わりに全文の200を返してください。Rangeリクエスト を参照してください。
Cf-Cache-Status は最初のリクエストで MISS で、以降も MISS です。
キャッシュが分割されている可能性が高いです。 キャッシュキーにはリクエストパス、対象エントリポイント、呼び出しの ctx.props が含まれます。見た目が同じ 2 つのリクエストでも、これらが違うとキャッシュキーが異なります。
よくある原因:
- リクエスト間で URL パスやクエリ文字列が違う(末尾のスラッシュも区別されます)。
- 呼び出し元の Worker がリクエストごとに異なる
ctx.propsを渡している(例: 異なるユーザー ID)。 - 同じ Worker の異なる 名前付きエントリポイント にリクエストが届いている。
Cloudflare は現在、キャッシュキーの構成を公開していないため、計算されたキーを直接見ることはできません。代わりに キャッシュキー に列挙された要素を順に確認し、両方のリクエストで同じであることを検証します。
デフォルト設定では想定どおりです。デフォルトでは Worker のバージョンがキャッシュキーの一部 なので、新しいバージョンはコールドキャッシュから始まり、前バージョンのキャッシュ済みレスポンスを再利用できません。デプロイ直後の最初のリクエストはミスになり、新しいバージョンのキャッシュが埋まるにつれてヒット率は回復します。
頻繁にデプロイし、デプロイ間でレスポンスがほとんど変わらない場合は、cache.cross_version_cache を有効にすると、バージョン間でキャッシュ済みレスポンスを共有し、デプロイのたびにキャッシュがリセットされるのを避けられます。トレードオフとして、キャッシュに影響する変更はすぐには適用されません。次のセクションを参照してください。
デフォルトでは、Worker のバージョンがキャッシュキーの一部で新しいバージョンはコールドキャッシュから始まるため、デプロイはすぐに効きます。前バージョンのレスポンスが見える場合は、バージョン間でキャッシュエントリを共有する cache.cross_version_cache が有効です。cross_version_cache を維持したままデプロイをすぐ反映するには:
- デプロイ後に
ctx.cache.purge({ purgeEverything: true })を呼びます。 いちばん簡単な方法です。 - バージョンメタデータバインディング で、キャッシュした各レスポンスに生成バージョンのタグを付け、 ロールバック時にそのタグをパージします。バージョン固有のパージ を参照してください。
オリジンのデータは変わったのに、リクエストがまだ古いコンテンツを返す場合:
- TTL を確認します。 レスポンスは
max-age秒間キャッシュされます。まだ鮮度ウィンドウ内のレスポンスを見ている可能性があります。 - 対象レスポンスをパージします。 タグまたはパスプレフィックス付きで
ctx.cache.purge()を使い、特定エントリを無効化します。キャッシュのパージ を参照してください。 - 書き込み時にタグを付けます。
Cache-Tagヘッダーを設定していないと、タグではパージできません。キャッシュするレスポンスにタグを付けてデプロイし、新しいエントリが書かれたあとでパージできるようになります。
呼び出し元ごとの認可コンテキストに ctx.props を使っていれば、これは起きません。起きている場合は、次のいずれかです。
- キャッシュキーに含まれないヘッダーやクエリパラメータで呼び出し元を認証している。認可入力を
ctx.propsに移します。ctx.propsによるマルチテナント安全性 を参照してください。 - サービスバインディングの呼び出しで、ユーザー固有のクエリパラメータが欠けている。クエリ文字列はキャッシュキーの一部です。呼び出し元ごとのリクエストパスが実際に違うことを確認します。
UPDATING は、stale なキャッシュからレスポンスを配信しつつ、バックグラウンドで Worker が更新していることを意味します。stale-while-revalidate を使っているときの想定どおりの挙動です。
想定より多く UPDATING が出る場合:
max-ageがリクエストの到着間隔より短い。max-age経過後に届くリクエストは、すべて再検証を起こします。max-age=0, stale-while-revalidate=<large>では、すべての リクエストが再検証を起こします。これは「常にキャッシュから配信する」挙動であり、「Worker を実行しない」ではありません。TTL と stale-while-revalidate の値を選ぶ を参照してください。
UPDATING が出るのは、次の すべて が真のときだけです。
- キャッシュエントリがあり、鮮度ウィンドウを過ぎている(stale)。
- レスポンスに
stale-while-revalidate=Nがあり、エントリが stale になってからN秒以内にリクエストが届く。 - レスポンスに
s-maxage、must-revalidate、proxy-revalidateが ない。
いずれかが偽だと、stale エントリへのリクエストはインライン再検証に落ち、EXPIRED(Worker が新しい本文を返した)または REVALIDATED(Worker が 304 Not Modified を返した)になります。
UPDATING が出ないよくある理由:
- レスポンスに
stale-while-revalidateディレクティブがない。 デフォルトの SWR ウィンドウは0なので、明示しないと stale リクエストはすべてフォアグラウンド再検証になります。 s-maxage、must-revalidate、またはproxy-revalidateがある。 RFC 9111 §4.2.4 ↗ では、これらのディレクティブは stale コンテンツの配信を禁じるため、いずれかがあると Cloudflare はstale-while-revalidate(とstale-if-error)を無効にします。stale 配信を使いたい場合は、エッジの鮮度ウィンドウにmax-ageを使います。- SWR ウィンドウが過ぎている。
max-age=60, stale-while-revalidate=120なら、エントリが stale になってから 120 秒以内のリクエストでUPDATINGが出ます。それ以降はインライン再検証に戻ります。
STALE は、キャッシュを更新するはずだったリクエストで Worker がエラーになったため、以前キャッシュしたレスポンスを Cloudflare が配信したことを意味します。たとえば Worker が例外を投げた、タイムアウトした、5xx を返した、などです。これは stale-if-error の挙動です。stale-if-error でエラー時に stale を配信する を参照してください。
STALE が出て想定外の場合:
- キャッシュの充填または再検証で Worker が失敗しています。 新しいレスポンスを出すべきリクエストのエラーを Workers observability ダッシュボード で確認します。クライアントが
5xxではなく stale レスポンスを見ているため、実際の障害が隠されています。 stale-if-errorを明示しておらず、レスポンスにs-maxage/must-revalidate/proxy-revalidateもない。 この場合、Cloudflare のデフォルトは、キャッシュエントリがパージされていない限り、Worker エラー時に期限なく stale レスポンスを配信することです。エラーをすぐクライアントに見せたい場合は、Cache-Controlにstale-if-error=0を設定します。詳細はstale-if-errorでエラー時に stale を配信する を参照してください。- 以前デプロイしたバージョンが配信されています。 修正をデプロイしても
STALEが出続ける場合、壊れたバージョンのキャッシュエントリがエラーのたびに配信されています。対象エントリを パージ し、現在のバージョンから新たに充填します。
クライアント側の可観測性で通常の HIT と STALE を区別するには、レスポンスと一緒に Cf-Cache-Status を記録します。Worker が失敗していてクライアントには見えていないことを示すシグナルは STALE だけです。
レスポンスが大きすぎてキャッシュできない場合、Cloudflare は保存しません。ほかはキャッシュ可能に見えても、毎回 Cf-Cache-Status: MISS になります。
プランごとのレスポンスサイズ上限は キャッシュ可能なサイズ上限 を参照してください。ローンチ時点では、Workers Caching のすべてのレスポンスに Free プランのサイズ上限が適用されます。詳細は レスポンスサイズ を参照してください。
ローンチ時点の主なデバッグ面は、Cf-Cache-Status レスポンスヘッダーと、Workers observability ダッシュボード の呼び出しごとのキャッシュヒット情報です。