Workers Caching は Worker ごとに、Wrangler 設定ファイルで設定します。有効にすると、キャッシュはすべての fetch() 呼び出しに適用されます。対象はエンドユーザーからのリクエスト、サービスバインディングの fetch()、ctx.exports 経由のエントリポイント間ループバック fetch() です。ただし、特定のエントリポイントで無効にする 場合を除きます。カスタムの RPC メソッド はキャッシュをバイパスします。
これは ご自身の Worker のキャッシュ です。Worker のコードと Wrangler ファイルで設定します。Worker がキャッシュを制御する方法は次だけです。
- Wrangler 設定の
cache.enabledフラグ。キャッシュのオン/オフを切り替えます。エントリポイントごと に上書きでき、バージョン間の挙動 も制御できます。 - Worker がレスポンスに付ける
Cache-Control(およびcdn-cache-control、cloudflare-cdn-cache-control)ヘッダー。RFC 9111 ↗ に従います。 - 一括パージ用の任意の
Cache-Tagレスポンスヘッダーと、プログラムからの無効化用のctx.cache.purge()。
設定面はこれだけです。
Wrangler 設定に cache ブロックを追加します。
{
"name": "my-worker",
"main": "src/index.ts",
// Set this to today's date
"compatibility_date": "2026-09-20",
"cache": {
"enabled": true,
},
}name = "my-worker"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-09-20"
[cache]
enabled = truecache.enabled を true にすると、Cloudflare はすべての HTTP リクエストで Worker を呼び出す前にキャッシュを確認します。これがすべてのエントリポイントのデフォルトです。exports でエントリポイントごとに上書きできます。
cache ブロックが受け付けるフィールドは 2 つです。必須の enabled と、任意の cross_version_cache です。それ以外のフィールドは将来用に予約されており、将来の Wrangler ではバリデーションエラーになることがあります。
キャッシュをオフにするには、cache.enabled を false にする(または cache ブロックを削除する)して再デプロイします。
{
"name": "my-worker",
"main": "src/index.ts",
// Set this to today's date
"compatibility_date": "2026-09-20",
"cache": {
"enabled": false,
},
}name = "my-worker"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-09-20"
[cache]
enabled = falseキャッシュを無効にしても、すでにキャッシュされたレスポンスはパージされません。以降のリクエストで Cloudflare がキャッシュを参照・書き込みしなくなるだけです。あとからキャッシュを再有効にすると、TTL 内のエントリは再び使えます。キャッシュ済みレスポンスの配信をすぐ止めたい場合は、無効化したあと キャッシュをパージ してください。
cache.enabled は Worker 全体のデフォルトです。ただし Worker は複数の エントリポイント を公開できます。デフォルトエクスポートと、任意の数の名前付き WorkerEntrypoint クラスです。それぞれ独立してキャッシュのオン/オフを切り替えられます。エントリポイント名をキーにした exports マップを使い、デフォルトエクスポートは "default" で指します。
{
"name": "my-worker",
"main": "src/index.ts",
// Set this to today's date
"compatibility_date": "2026-09-20",
"cache": {
"enabled": true,
},
"exports": {
// Opt the default entrypoint out of caching.
"default": { "type": "worker", "cache": { "enabled": false } },
// Keep caching on for the Admin entrypoint.
"Admin": { "type": "worker", "cache": { "enabled": true } },
},
}name = "my-worker"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-09-20"
[cache]
enabled = true
[exports.default]
type = "worker"
[exports.default.cache]
enabled = false
[exports.Admin]
type = "worker"
[exports.Admin.cache]
enabled = true各エントリは { "type": "worker", "cache": { "enabled": <boolean> } } です。エントリポイント単位の cache.enabled は、そのエントリポイントのトップレベル cache.enabled を上書きします。一覧にないエントリポイントはトップレベルの値を継承します。トップレベルの cache ブロックなしで、対象のエントリポイントだけを列挙してキャッシュを有効にすることもできます。
Worker のコードを変えずに、特定のエントリポイントだけをオプトイン / オプトアウト できます。
- エントリポイントをオプトアウトする と、そのエントリポイントはすべてのリクエストで実行されます。認証、正規化、ディスパッチを行い、自身はキャッシュから配信すべきでないゲートウェイやルーターのエントリポイントに向きます。ゲートウェイパターン を作る推奨方法は、ゲートウェイのエントリポイントでキャッシュを無効にし、ゲートウェイが
ctx.exportsで呼ぶ内側のエントリポイントで有効にすることです。 - エントリポイントをオプトインする と、再利用可能なレスポンスを返すエントリポイントだけをキャッシュし、残りの Worker はキャッシュしません。
cache 設定は Worker バージョンの一部です。
wrangler deployまたはwrangler versions uploadでアップロードした各バージョンは、その Wrangler 設定にあるcache.enabledの値を取り込みます。- 以前のバージョンにロールバックすると、そのバージョンに付いていた
cache設定も戻ります。 - 段階的デプロイ を使うと、キャッシュを一部のトラフィックだけで有効にしてから 100% に広げられます。キャッシュ無効のバージョンから有効なバージョンへの段階的ロールアウト中、旧バージョンに振り分けられたトラフィックはこれまでどおりキャッシュなしで動き、新バージョンに振り分けられたトラフィックはキャッシュを参照・書き込みします。デフォルトでは Worker のバージョンがキャッシュキーの一部なので、2 つのバージョンは独立したキャッシュエントリを持ち、互いのレスポンスを配信しません。バージョン間キャッシュ を参照してください。
デフォルトでは、Worker のバージョンはキャッシュキーの一部 です。デプロイした各バージョンは独立したキャッシュを持つため、新しいデプロイは空のキャッシュから始まり、以前のバージョンが書いたレスポンスを配信しません。これがデフォルトなのは、いちばん分かりやすいからです。新しいデプロイはすぐ効き、置き換えられたバージョンが作ったレスポンスは配信されません。
トレードオフは、デプロイのたびにキャッシュヒット率がリセットされる ことです。新しいバージョンは前バージョンのキャッシュ済みレスポンスを再利用できないため、デプロイ直後の最初のリクエストはミスになり、新しいバージョンのキャッシュが埋まるまで続きます。デプロイ直後に Worker のキャッシュヒット率が下がるいちばん多い理由です。
キャッシュヒット率を最大化し、キャッシュに影響する変更の反映が遅くなることを受け入れられる場合は、cross_version_cache を true にします。キャッシュ済みレスポンスはバージョン間で共有されます。あるバージョンが書いたレスポンスは、TTL が切れていなければ後のバージョンからも配信できます。
{
"name": "my-worker",
"main": "src/index.ts",
// Set this to today's date
"compatibility_date": "2026-09-20",
"cache": {
"enabled": true,
"cross_version_cache": true,
},
}name = "my-worker"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-09-20"
[cache]
enabled = true
cross_version_cache = true頻繁にデプロイし、ほとんどのデプロイでレスポンスが変わらない場合は、cross_version_cache の有効化を検討してください。デプロイのたびに温まったキャッシュを捨てずに済みます。代償として、デプロイだけではキャッシュは無効化されません。レスポンス内容を変える変更のあと、古いキャッシュ済みレスポンスは期限切れか パージ するまで配信され続けます。段階的デプロイ 中は両バージョンが 1 つのキャッシュを共有します。cross_version_cache を有効にしたままデプロイをすぐ反映したい場合は、デプロイ後にキャッシュをパージするか、レスポンスにバージョンでタグを付けます。デプロイをまたいだキャッシュの無効化 を参照してください。
cross_version_cache が効くのは、キャッシュが有効なときだけです。キャッシュがオンのすべてのエントリポイントに適用されます。
cache ブロックはトップレベルで設定し、環境 ごとに上書きできます。よくあるパターンは、安全だと確認できてから本番でキャッシュを有効にし、デバッグしやすいようステージングはキャッシュなしのままにすることです。
{
"name": "my-worker",
"main": "src/index.ts",
// Set this to today's date
"compatibility_date": "2026-09-20",
"cache": {
"enabled": false,
},
"env": {
"production": {
"cache": {
"enabled": true,
},
},
},
}name = "my-worker"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-09-20"
[cache]
enabled = false
[env.production.cache]
enabled = trueキャッシュを有効にすると、Worker が Cloudflare のキャッシュに対するオリジンになります。Worker が返すレスポンスの標準 HTTP Cache-Control ディレクティブが、Cloudflare がキャッシュするか、どれだけキャッシュするかを決めます。ディレクティブの一覧と相互作用は Cache-Control を参照してください。
max-age で、レスポンスを鮮度あり(fresh)とみなす期間を制御します。
export default {
async fetch(request) {
const body = await renderPage(request);
return new Response(body, {
headers: {
"Content-Type": "text/html",
// Cached for 1 hour at Cloudflare's edge and in the browser.
"Cache-Control": "public, max-age=3600",
},
});
},
};
// Replace with your own rendering logic.
async function renderPage(request) {
return `<!doctype html><title>Home</title><h1>Hello</h1>`;
}export default {
async fetch(request): Promise<Response> {
const body = await renderPage(request);
return new Response(body, {
headers: {
"Content-Type": "text/html",
// Cached for 1 hour at Cloudflare's edge and in the browser.
"Cache-Control": "public, max-age=3600",
},
});
},
} satisfies ExportedHandler;
// Replace with your own rendering logic.
async function renderPage(request: Request): Promise<string> {
return `<!doctype html><title>Home</title><h1>Hello</h1>`;
}ブラウザーとエッジでキャッシュ期間を変えたい場合は、エッジ専用のディレクティブに cdn-cache-control(または cloudflare-cdn-cache-control)を使い、ブラウザー向けには Cache-Control を残します。後述の ヘッダーの優先順位 を参照してください。
キャッシュ済みレスポンスが stale になると、stale-while-revalidate により Cloudflare は stale レスポンスをすぐ返し、バックグラウンドで更新します。
export default {
async fetch(request) {
const data = { timestamp: Date.now() };
return new Response(JSON.stringify(data), {
headers: {
"Content-Type": "application/json",
// Fresh for 10 minutes; may be served stale for up to 1 minute
// while a background revalidation runs.
"Cache-Control": "public, max-age=600, stale-while-revalidate=60",
},
});
},
};export default {
async fetch(request): Promise<Response> {
const data = { timestamp: Date.now() };
return new Response(JSON.stringify(data), {
headers: {
"Content-Type": "application/json",
// Fresh for 10 minutes; may be served stale for up to 1 minute
// while a background revalidation runs.
"Cache-Control": "public, max-age=600, stale-while-revalidate=60",
},
});
},
} satisfies ExportedHandler;高いキャッシュヒット率と高い鮮度はトレードオフです。バックグラウンド再検証はキャッシュ更新のレイテンシを隠しますが、再検証のたびに Worker は 1 回実行されます。無料ではありません。
よくある 2 つのパターンです。
- ほとんど静的で、少しの staleness を許容できるコンテンツ。 短い
max-age(例: 60 秒)と、長いstale-while-revalidateウィンドウ(例: 3600 秒)を使います。ほとんどのリクエストはHITで、ときどきバックグラウンド更新が走ります。 - 高トラフィックのエンドポイントで「常にキャッシュから配信する」。
max-age=0, stale-while-revalidate=<large>を使います。すべてのリクエストは以前キャッシュしたレスポンスをすぐ返し、バックグラウンド更新を起こします。再検証のため Worker はリクエストごとに 1 回実行されるので、CPU コストは毎回 Worker を動かす場合に近くなります。リクエスト量が減ると鮮度も落ちます。長いあいだリクエストが来ないと、次のリクエストは stale コンテンツを見ます。
stale-if-error を使うと、期限切れキャッシュエントリの更新中に Worker が失敗したとき(例外、タイムアウト、5xx など)、Cloudflare は以前キャッシュしたレスポンスを返せます。一時的な Worker 障害からクライアントを守れます。
"Cache-Control": "public, max-age=600, stale-if-error=86400",Worker が新しいレスポンスを作っているあいだ、stale-if-error は効きません。期限切れエントリの更新中に Worker が失敗すると、Cloudflare は最後に成功したキャッシュ済みレスポンスを Cf-Cache-Status: STALE 付きで、stale-if-error のウィンドウまで配信します。真のキャッシュミス(以前のエントリがない)では、配信する stale がないため stale-if-error は使えません。その場合、Worker のエラーはそのままクライアントに届きます。
複数のキャッシュヘッダーがある場合、いちばん具体的なものが優先されます。
cloudflare-cdn-cache-control— Cloudflare 固有で、最優先です。Cloudflare が消費し、クライアントへ返すレスポンスからは取り除きます。cdn-cache-control— CDN 専用ディレクティブの標準ヘッダーです。Cloudflare が尊重し、下流の CDN にも渡します。Cache-Control— 標準 HTTP ヘッダーです。Cloudflare が尊重し、クライアントにも渡します。
ブラウザーに見せるより長いエッジ TTL が必要で、ディレクティブを下流に漏らしたくない場合に cloudflare-cdn-cache-control を使います。
通常、呼び出し先がレスポンスに Cache-Control を付けて、キャッシュ方法を決めます。あるエントリポイントが ctx.exports のループバックで別のキャッシュ対象エントリポイントを呼ぶとき、呼び出し元 のエントリポイントは、リクエストに cf.cacheControl を付けて、その呼び出しの Cache-Control ディレクティブを渡せます。
次の例では、Backend エントリポイントは自身で Cache-Control を付けません。デフォルトエントリポイントが ctx.exports で Backend を呼ぶときにキャッシュ方針を決めます。
import { WorkerEntrypoint } from "cloudflare:workers";
// Cached entrypoint. It does not set Cache-Control itself.
export class Backend extends WorkerEntrypoint {
async fetch(request) {
return new Response("Hello from the backend", {
headers: { "Content-Type": "text/html" },
});
}
}
// Gateway entrypoint. Caches the Backend's response for this call for
// 5 minutes, without the Backend needing to set Cache-Control itself.
export default {
async fetch(request, env, ctx) {
return ctx.exports.Backend.fetch(request, {
cf: { cacheControl: "public, max-age=300" },
});
},
};import { WorkerEntrypoint } from "cloudflare:workers";
// Cached entrypoint. It does not set Cache-Control itself.
export class Backend extends WorkerEntrypoint<Env> {
async fetch(request: Request): Promise<Response> {
return new Response("Hello from the backend", {
headers: { "Content-Type": "text/html" },
});
}
}
// Gateway entrypoint. Caches the Backend's response for this call for
// 5 minutes, without the Backend needing to set Cache-Control itself.
export default {
async fetch(request, env, ctx): Promise<Response> {
return ctx.exports.Backend.fetch(request, {
cf: { cacheControl: "public, max-age=300" },
});
},
} satisfies ExportedHandler<Env>;Cloudflare は cf.cacheControl を、その呼び出しで呼び出し先のレスポンスをキャッシュするための信頼できる Cache-Control ディレクティブとして扱います。値は標準の Cache-Control 文字列で、このページで説明する 同じディレクティブのセマンティクス に従います。max-age、stale-while-revalidate、no-store などです。これにより、呼び出し元のエントリポイントは、呼び出し先のコードを変えずにキャッシュ方法を決められます。
カスタムキャッシュキー と同様、cf.cacheControl が尊重されるのは、アカウント内に留まる呼び出しだけです。リクエストがアカウント境界を越えると Cloudflare は cf オブジェクトを落とすため、あるアカウントの呼び出し元が、別アカウントの Worker のキャッシュ方法を変えることはできません。このディレクティブはエンドユーザーからのリクエストにも効きません。受信リクエストの cf オブジェクトはクライアントではなく Cloudflare が埋めるからです。
すべてのレスポンスに、そのリクエストで何が起きたかを示す Cf-Cache-Status ヘッダーが付きます。よく見る値は HIT、MISS、EXPIRED、REVALIDATED、UPDATING、STALE、BYPASS です。値の一覧と意味は Cloudflare のキャッシュレスポンス を参照してください。
Cache-Tag レスポンスヘッダーは、キャッシュ済みレスポンスにタグを付け、あとから一括パージできるようにします。Cloudflare はこのヘッダーを消費し、クライアントに届く前に取り除きます。
export default {
async fetch(request) {
const html = `<!doctype html><title>Post</title>`;
return new Response(html, {
headers: {
"Content-Type": "text/html",
"Cache-Control": "public, max-age=3600",
"Cache-Tag": "blog,posts,post-123",
},
});
},
};export default {
async fetch(request): Promise<Response> {
const html = `<!doctype html><title>Post</title>`;
return new Response(html, {
headers: {
"Content-Type": "text/html",
"Cache-Control": "public, max-age=3600",
"Cache-Tag": "blog,posts,post-123",
},
});
},
} satisfies ExportedHandler;Cache-Tag ヘッダーの値は、カンマ区切りのタグ一覧です。ゾーンキャッシュと同じ制限が適用されます。一覧は Cache タグの制限 を参照してください。覚えておくべき主な制約は次のとおりです。
- タグの値は 印字可能な ASCII(
0x21–0x7E)である必要があります。スペース、Unicode、制御文字は使えません。 - 各タグは最大 1024 文字 です。
- パージ用に、1 レスポンスあたり最大 1000 タグ を付けられます。
- パージ時のタグ照合は 大文字小文字を区別しません。
Fooとfooは同じレスポンス集合をパージします。
無効なタグ(長さ超過、スペース含む、非 ASCII 文字含む)はキャッシュ保存時に黙って落とされます。レスポンスは残りの有効なタグ付きでキャッシュされますが、どのタグが落ちたかを知る手段はありません。重要な場合は、返す前に Worker 内でタグを検証してください。
Workers Caching は Cloudflare の標準的な キャッシュバイパスルール を継承します。よくあるきっかけは次のとおりです。
- レスポンスに
Set-Cookieヘッダーがある(ただしCache-Controlにprivate="set-cookie"またはno-cache="set-cookie"がある場合を除く。その場合、キャッシュコピーからSet-Cookieは取り除かれます)。 - リクエストに
Authorizationヘッダーがある。レスポンスが保存されるのは、Cache-Controlにpublic、must-revalidate、またはs-maxageがある場合だけです。RFC 9111 §3.5 ↗ に従います。 - レスポンスの
Cache-Controlヘッダーにprivateまたはno-storeがある。
いずれかに該当すると、Cf-Cache-Status は BYPASS になり、Worker はすべてのリクエストで実行されます。
明示的な Cache-Control ディレクティブがあっても、保存されないステータスコードがあります。
520–526(Cloudflare のフェイルセーフレスポンス)は一時的なエラーとして扱われ、キャッシュされません。
Workers Caching は、キャッシュした全文レスポンスから Range リクエストに応えます。Worker 側でバイト範囲の切り出しを実装する必要はありません。
クライアントが Range リクエストを送ると、Cloudflare は Worker を呼ぶ前に Range ヘッダーを取り除き、Worker に全文を求めます。Worker は通常どおり Cache-Control 付きの 200 を返します。Cloudflare はその全文を保存し、要求されたバイト範囲を切り出して、クライアントに 206 Partial Content(範囲が無効なら 416 Range Not Satisfiable)で返します。同じ URL への以降の Range リクエストは、キャッシュエントリだけから満たされます。Worker は呼ばれず、Cf-Cache-Status は HIT です。
たとえば、コールドキャッシュに対する Range: bytes=0-9 の GET は、行きは MISS になり(Worker が実行され全文を返す)、最初の 10 バイト付きの 206 を返します。同じ URL への後続の GET Range: bytes=10-19 は HIT で、Worker を呼ばずにキャッシュからその 10 バイトを返します。
Worker 自身が 206 を返す場合(Worker 内で Range 処理を実装した場合など)、Cloudflare はそれをキャッシュ不可として保存しません。全文の 200 を返し、範囲の切り出しは Workers Caching に任せてください。
Worker が Vary レスポンスヘッダーを返すと、Cloudflare は列挙したリクエストヘッダー値の組み合わせごとに別のキャッシュバリアントを保存し、保存値が受信リクエストと一致するバリアントだけを返します。これは RFC 9110 ↗ と RFC 9111 ↗ のキャッシュキー計算を実装しています。コード例付きの導入は Vary によるコンテンツネゴシエーション を参照してください。
Workers Caching での Vary の処理は次のとおりです。
- すべてのヘッダー名が尊重されます。 Worker が
Varyに列挙したヘッダー名は、バリアントキーに含まれます。許可リストはありません。 - 値は文字どおり比較されます。 Cloudflare はキー付けの前に、列挙されたリクエストヘッダーを正規化しません。
Accept-Encoding: gzip, brとAccept-Encoding: br, gzipは意味が同じでも、別のバリアントになります。同等の値を同じバリアントにまとめたい場合は、ゲートウェイ Worker でヘッダーを正規化してから渡すか、Varyを付ける Worker 内で正規化してください。 Vary: *はキャッシュを無効にします。 ワイルドカードの分散はリクエストヘッダーから決定的に満たせないため、レスポンスはキャッシュ不可として扱われ、Cf-Cache-StatusはBYPASSです。- バリアントは 1 つのパージ識別子を共有します。 タグまたはパスプレフィックスによる パージ は、その URL のすべてのバリアントをまとめて無効化します。そのため、すべてのバリアントは同じ
Cache-Tag値を使う必要があります。バリアントごとに違うタグを付けると、パージが不整合になります。 - 画像変換機能が優先されます。 Polish または Image Resizing が作ったレスポンスは、すでに独自のバリアントを持つため、それらの
Varyは無視されます。
Worker が自身のコンテンツネゴシエーションを制御します。Worker がレスポンスに付けた Content-Encoding が、Cloudflare が保存し以降のリクエストに配信する内容です。
クライアントごとに異なるエンコーディングを返す必要がある場合、選択肢は 2 つです。
- Worker 内で正規のエンコーディングを 1 つ選ぶ。
Accept-Encodingリクエストヘッダーをもとに決め、本文を一度エンコードして、1 つの表現を返します。以降のその URL へのリクエストは、受け入れる内容に関係なく同じキャッシュエントリにヒットします。キャッシュヒット率はいちばん高くなりますが、どのクライアントにどのエンコーディングを出すかを決める必要があります。 Accept-EncodingでVaryする。 リクエストごとに異なるContent-Encodingを返し、Vary: Accept-Encodingを付けます。Cloudflare は、Worker が見たAccept-Encoding値ごとに 1 つのバリアントを保存します。比較は文字どおりなので、意味が同じでも順序や quality factor が違う値を送るクライアントは別バリアントになります。レスポンス生成前にAccept-Encodingを正規化(例: ゲートウェイ Worker)し、キャッシュの分裂を抑えてください。