Worker は、パージ API を使って、自身のキャッシュ済みレスポンスをいつでも無効化できます。データが変わり、キャッシュを出し続ける性能上の利点より新しい値のほうが重要なときに使います。コンテンツ更新、ユーザー操作、上流システムからの webhook のあとなどが該当します。
Workers Caching は Worker 自身のキャッシュ なので、パージの範囲はそのキャッシュを所有する Worker に限られます。Worker 内では、さらに purge() を呼んだ エントリポイント に限定されます。ある Worker が別の Worker のキャッシュに手を入れることはできません。あるエントリポイントが別のエントリポイントのキャッシュに手を入れることもできません。ダッシュボード、API、Terraform によるゾーンレベルのパージも、Workers Caching の内容には影響しません。
Worker 内からパージを起こす方法は、同等の 2 通りです。
ctx.cache.purge(...)— すべてのハンドラーに渡される実行コンテキストで使えます。すでにctxがスコープ内にあるときに使います。cache.purge(...)—cloudflare:workersからインポートします。ctxを受け取らないコードからパージしたいときに使います。複数ハンドラーで共有するユーティリティモジュールや、実行コンテキストを内部へ渡さないフレームワークアダプターなどが該当します。
どちらも同じ API を呼び、動作は同じです。コードが読みやすいほうを選びます。
import { cache } from "cloudflare:workers";
export default {
async fetch(request, env, ctx) {
// Using the module import — no need to thread ctx through helper functions.
await cache.purge({ tags: ["blog-posts"] });
// Equivalent, using ctx directly:
// await ctx.cache.purge({ tags: ["blog-posts"] });
return new Response("Purged", { status: 200 });
},
};import { cache } from "cloudflare:workers";
export default {
async fetch(request, env, ctx): Promise<Response> {
// Using the module import — no need to thread ctx through helper functions.
await cache.purge({ tags: ["blog-posts"] });
// Equivalent, using ctx directly:
// await ctx.cache.purge({ tags: ["blog-posts"] });
return new Response("Purged", { status: 200 });
},
} satisfies ExportedHandler;以降の例の多くは、すでに ctx がスコープ内にあるため ctx.cache.purge(...) を使います。インポート形式を使う場合は cache.purge(...) に置き換えてください。ほかは変わりません。
purge() には、単独の purgeEverything: true、または tags と pathPrefixes の一方または両方を渡します。
| フィールド | パージ対象 | スコープ |
|---|---|---|
tags |
Cache-Tag で指定した値のいずれかが付いた、キャッシュ済みレスポンスすべて |
エントリポイントごと |
pathPrefixes |
リクエストパスが指定プレフィックスのいずれかで始まる、キャッシュ済みレスポンスすべて | エントリポイントごと |
purgeEverything |
purge() を呼んだエントリポイントの、キャッシュ済みレスポンスすべて |
エントリポイントごと |
purgeEverything は排他です。tags と pathPrefixes は 1 回の呼び出しで組み合わせられますが、purgeEverything と同時には渡しません。
3 つのモードはいずれも、purge() を呼んだ エントリポイント に限定されます。PublicAPI からのパージは、タグ名やパスプレフィックスが同じでも、AdminAPI が保存したキャッシュ済みレスポンスには影響しません。Worker のすべてのエントリポイントで無効化するには、各エントリポイントから purge() を呼びます。
返される Promise は結果オブジェクトに解決します。成功の確認や失敗の扱いは 戻り値 を参照してください。
書き込みのあとにパージするには、データを変更するハンドラーの末尾で ctx.cache.purge() を呼びます。
export default {
async fetch(request, env, ctx) {
if (request.method === "POST") {
const body = await request.json();
// Mutate your data source (D1, KV, an origin, and so on), then invalidate
// every cached response tagged for this post.
await ctx.cache.purge({
tags: [`post-${body.postId}`, "post-list"],
});
return new Response("Updated", { status: 200 });
}
// Handle cacheable reads here.
return new Response("Hello", {
headers: { "Cache-Control": "public, max-age=3600" },
});
},
};export default {
async fetch(request, env, ctx): Promise<Response> {
if (request.method === "POST") {
const body = await request.json<{ postId: string }>();
// Mutate your data source (D1, KV, an origin, and so on), then invalidate
// every cached response tagged for this post.
await ctx.cache.purge({
tags: [`post-${body.postId}`, "post-list"],
});
return new Response("Updated", { status: 200 });
}
// Handle cacheable reads here.
return new Response("Hello", {
headers: { "Cache-Control": "public, max-age=3600" },
});
},
} satisfies ExportedHandler;フィールドは 1 回の呼び出しで組み合わせられます。たとえば purge({ tags: ["blog-posts"], pathPrefixes: ["/blog/"] }) は、タグまたはパスプレフィックスの いずれか に一致するものをすべてパージします。フィールドは和集合であり、積集合ではありません。1 つの論理的な無効化が、複数の方式でタグ付けされたレスポンスに影響する場合に使います。
export default {
async fetch(request, env, ctx) {
// Combined call: invalidates everything tagged "blog-posts" AND
// everything under /blog/ in a single round-trip.
await ctx.cache.purge({
tags: ["blog-posts"],
pathPrefixes: ["/blog/"],
});
return new Response("Purged", { status: 200 });
},
};export default {
async fetch(request, env, ctx): Promise<Response> {
// Combined call: invalidates everything tagged "blog-posts" AND
// everything under /blog/ in a single round-trip.
await ctx.cache.purge({
tags: ["blog-posts"],
pathPrefixes: ["/blog/"],
});
return new Response("Purged", { status: 200 });
},
} satisfies ExportedHandler;タグは Cache-Tag レスポンスヘッダーでレスポンスに付け、あとから名前でパージします。いちばん柔軟で、よく使われる方法です。
export default {
async fetch(request) {
const url = new URL(request.url);
const postId = url.pathname.split("/").pop() ?? "unknown";
const body = { id: postId, title: `Post ${postId}` };
return new Response(JSON.stringify(body), {
headers: {
"Content-Type": "application/json",
"Cache-Control": "public, max-age=3600",
"Cache-Tag": `post,post-${postId},blog`,
},
});
},
};export default {
async fetch(request): Promise<Response> {
const url = new URL(request.url);
const postId = url.pathname.split("/").pop() ?? "unknown";
const body = { id: postId, title: `Post ${postId}` };
return new Response(JSON.stringify(body), {
headers: {
"Content-Type": "application/json",
"Cache-Control": "public, max-age=3600",
"Cache-Tag": `post,post-${postId},blog`,
},
});
},
} satisfies ExportedHandler;Cache-Tag ヘッダーの値は、カンマ区切りのタグ一覧です。Cloudflare は、クライアントへレスポンスを返す前にこのヘッダーを取り除きます。
タグの値は 印字可能な ASCII(空白なし、Unicode なし)である必要があります。各タグは最大 1024 文字、1 レスポンスあたり最大 1000 タグ です。パージ時のタグ照合は 大文字小文字を区別しません。Foo と foo は同じレスポンス集合をパージします。無効なタグは保存時に黙って捨てられ、残りの有効なタグ付きでレスポンスはキャッシュされます。一覧は キャッシュタグの制限 を参照してください。
export default {
async fetch(request, env, ctx) {
const postId = new URL(request.url).searchParams.get("id");
if (!postId) return new Response("Missing id", { status: 400 });
await ctx.cache.purge({ tags: [`post-${postId}`] });
return new Response("Purged", { status: 200 });
},
};export default {
async fetch(request, env, ctx): Promise<Response> {
const postId = new URL(request.url).searchParams.get("id");
if (!postId) return new Response("Missing id", { status: 400 });
await ctx.cache.purge({ tags: [`post-${postId}`] });
return new Response("Purged", { status: 200 });
},
} satisfies ExportedHandler;タグは、purge() を呼んだエントリポイントに限定されます。2 つのエントリポイントのレスポンスに付いた user-42 というタグは、1 回の purge({ tags: ["user-42"] }) では 無効化されません。呼び出し元のエントリポイントにだけ効きます。同じタグを複数のエントリポイントで無効化する必要がある場合は、各エントリポイントから purge() を呼ぶか、あとで無効化したいレスポンスをすべてキャッシュする共有エントリポイントにパージ呼び出しをまとめます。
関連するレスポンス群を 1 回の呼び出しで無効化するには、所属する階層の各レベルを表す複数のタグを付けます。いわゆる「ソフトタグ」です。
export default {
async fetch(request) {
const path = new URL(request.url).pathname;
// Build a list of hierarchical tags for the current path.
// A response at /blog/2025/02/hello gets tags for:
// _path:/blog/, _path:/blog/2025/, _path:/blog/2025/02/, _path:/blog/2025/02/hello
const segments = path.split("/").filter(Boolean);
const tags = segments.map(
(_, i) => `_path:/${segments.slice(0, i + 1).join("/")}/`,
);
const body = `<!doctype html><title>${path}</title>`;
return new Response(body, {
headers: {
"Content-Type": "text/html",
"Cache-Control": "public, max-age=3600",
"Cache-Tag": tags.join(","),
},
});
},
};export default {
async fetch(request): Promise<Response> {
const path = new URL(request.url).pathname;
// Build a list of hierarchical tags for the current path.
// A response at /blog/2025/02/hello gets tags for:
// _path:/blog/, _path:/blog/2025/, _path:/blog/2025/02/, _path:/blog/2025/02/hello
const segments = path.split("/").filter(Boolean);
const tags = segments.map(
(_, i) => `_path:/${segments.slice(0, i + 1).join("/")}/`,
);
const body = `<!doctype html><title>${path}</title>`;
return new Response(body, {
headers: {
"Content-Type": "text/html",
"Cache-Control": "public, max-age=3600",
"Cache-Tag": tags.join(","),
},
});
},
} satisfies ExportedHandler;タグ _path:/blog/2025/ をパージすると、URL が /blog/2025/ で始まるキャッシュ済みレスポンスがすべて無効になります。
タグの数、長さ、文字種の制限は キャッシュタグの制限 を参照してください。
デフォルトでは、Workers Caching は Worker のバージョンごとにキャッシュを分割 します。各デプロイはコールドキャッシュから始まるため、バージョン単位のパージは通常不要です。この節は、バージョン間でキャッシュ済みレスポンスを共有する cache.cross_version_cache を有効にした場合だけが対象です。その場合、バージョン A が書いたレスポンスが、バージョン B のデプロイ後も配信されることがあります。ロールバック後など、特定バージョンが書いたエントリだけをパージしたくなることがあります。そのためには、各レスポンスに生成したバージョンのタグを付け、あとでそのタグをパージします。
Wrangler 設定に version metadata バインディング を追加します。
{
"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 },
"version_metadata": { "binding": "CF_VERSION_METADATA" },
}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
[version_metadata]
binding = "CF_VERSION_METADATA"タグの先頭にバージョン ID を付けます。
export default {
async fetch(request, env, ctx) {
const { id: versionId } = env.CF_VERSION_METADATA;
const postId = new URL(request.url).pathname.split("/").pop() ?? "unknown";
return new Response(JSON.stringify({ id: postId }), {
headers: {
"Content-Type": "application/json",
"Cache-Control": "public, max-age=3600",
// Include the version ID as a tag so you can purge by version later.
"Cache-Tag": `post,post-${postId},v:${versionId}`,
},
});
},
};interface Env {
CF_VERSION_METADATA: WorkerVersionMetadata;
}
export default {
async fetch(request, env, ctx): Promise<Response> {
const { id: versionId } = env.CF_VERSION_METADATA;
const postId = new URL(request.url).pathname.split("/").pop() ?? "unknown";
return new Response(JSON.stringify({ id: postId }), {
headers: {
"Content-Type": "application/json",
"Cache-Control": "public, max-age=3600",
// Include the version ID as a tag so you can purge by version later.
"Cache-Tag": `post,post-${postId},v:${versionId}`,
},
});
},
} satisfies ExportedHandler<Env>;ロールバック後など、特定バージョンが書いたものをすべて無効化したいときは、バージョンタグをパージします。
export default {
async fetch(request, env, ctx) {
const versionId = new URL(request.url).searchParams.get("version");
if (!versionId) return new Response("Missing version", { status: 400 });
await ctx.cache.purge({ tags: [`v:${versionId}`] });
return new Response("Purged", { status: 200 });
},
};export default {
async fetch(request, env, ctx): Promise<Response> {
const versionId = new URL(request.url).searchParams.get("version");
if (!versionId) return new Response("Missing version", { status: 400 });
await ctx.cache.purge({ tags: [`v:${versionId}`] });
return new Response("Purged", { status: 200 });
},
} satisfies ExportedHandler;pathPrefixes は、リクエストパス が指定プレフィックスのいずれかで始まるキャッシュ済みレスポンスをすべて無効化します。
export default {
async fetch(request, env, ctx) {
// Invalidate everything under /blog/2025/ for the current entrypoint.
await ctx.cache.purge({
pathPrefixes: ["/blog/2025/"],
});
return new Response("Purged", { status: 200 });
},
};export default {
async fetch(request, env, ctx): Promise<Response> {
// Invalidate everything under /blog/2025/ for the current entrypoint.
await ctx.cache.purge({
pathPrefixes: ["/blog/2025/"],
});
return new Response("Purged", { status: 200 });
},
} satisfies ExportedHandler;pathPrefixes の各項目は パス であり、完全な URL ではありません。プレフィックスにスキーム、ホスト、クエリ文字列、フラグメントを含めてはいけません。https://example.com/blog/ のような値は無効な入力であり、「一致しないプレフィックス」ではありません。先頭のスラッシュは任意です(/images と images は同じ扱い)が、分かりやすさのため付けることを推奨します。
pathPrefixes は、パージを呼んだエントリポイントに限定されます。PublicAPI からの purge({ pathPrefixes: ["/blog/"] }) は、パスが /blog/ で始まっていても、AdminAPI が保存したキャッシュ済みレスポンスには影響しません。
「URL でパージする」専用モードはありません。キャッシュ済みの 1 URL を無効化するには、そのパスを要素 1 つの pathPrefixes 配列として渡します。
export default {
async fetch(request, env, ctx) {
// Invalidate the cached response for exactly /blog/2026/hello-world.
await ctx.cache.purge({
pathPrefixes: ["/blog/2026/hello-world"],
});
return new Response("Purged", { status: 200 });
},
};export default {
async fetch(request, env, ctx): Promise<Response> {
// Invalidate the cached response for exactly /blog/2026/hello-world.
await ctx.cache.purge({
pathPrefixes: ["/blog/2026/hello-world"],
});
return new Response("Purged", { status: 200 });
},
} satisfies ExportedHandler;pathPrefixes はリクエストパスの先頭に一致するため、フルパスを渡すとそのパスに一致します。さらにそのパスを延長したパス(例: /blog/2026/hello-world-2)にも一致します。過剰パージを避けて完全一致にしたい場合は、代わりに タグ を使います。
呼び出し元エントリポイントが保存したキャッシュ済みレスポンスを、すべて無効化します。
export default {
async fetch(request, env, ctx) {
await ctx.cache.purge({ purgeEverything: true });
return new Response("Purged", { status: 200 });
},
};export default {
async fetch(request, env, ctx): Promise<Response> {
await ctx.cache.purge({ purgeEverything: true });
return new Response("Purged", { status: 200 });
},
} satisfies ExportedHandler;多用は避けてください。すべてをパージすると、再充填されるまで後続リクエストはキャッシュミスになり、Worker と呼び出す上流サービスへの負荷が一時的に増えます。
ctx.cache.purge() によるパージは、Cloudflare の Instant Purge 基盤を使い、ゾーンレベルのパージと同じ保証で世界中に伝播します。
purge() は結果オブジェクトに解決します。受け入れられたかは success で確認し、失敗時は errors を調べます。
export default {
async fetch(request, env, ctx) {
const result = await ctx.cache.purge({ tags: ["blog-posts"] });
if (!result.success) {
console.error("Cache purge failed", result.errors);
return new Response("Purge failed", { status: 500 });
}
return new Response("Purged", { status: 200 });
},
};export default {
async fetch(request, env, ctx): Promise<Response> {
const result = await ctx.cache.purge({ tags: ["blog-posts"] });
if (!result.success) {
console.error("Cache purge failed", result.errors);
return new Response("Purge failed", { status: 500 });
}
return new Response("Purged", { status: 200 });
},
} satisfies ExportedHandler;失敗時、errors の各エラーには数値の code と、ログや呼び出し元へ出せる人が読める message が付きます。
purge() は、Cloudflare のゾーンパージ API と同じレート制限を使います。ただし Workers Caching はゾーンではなく Worker に紐づくため、アカウントやゾーンのプランに関係なく、Workers Cache は常に 提供状況と制限 に記載の Free プランの上限 を使います。レート制限にかかると success は false になり、errors に拒否の内容が入ります。