← Cloudflare Workers / workers / cache
Очистка кеша
Worker может в любой момент сбросить собственные закэшированные ответы с помощью purge API. Сброс кэша полезен, когда данные изменились и новое значение важнее, чем выигрыш в производительности от дальнейшей отдачи закэшированного ответа, например после обновления контента, действия пользователя или вебхука от вышестоящей системы.
Поскольку Workers Caching является кэш вашего Worker, очистка кеша ограничена Worker, которому принадлежит кеш. Внутри Worker очистка дополнительно ограничена entrypoint который вызвал purge(). Worker не может обращаться к кешу другого Worker, entrypoint не может обращаться к кешу другого entrypoint, а очистка на уровне зоны (через панель управления, API, либо Terraform) влияет на содержимое кеша Workers Caching.
Два способа вызвать очистку
Есть два равнозначных способа инициировать очистку кеша из вашего Worker:
ctx.cache.purge(...): доступен в контексте выполнения, который передаётся каждому обработчику. Используйте это, если у вас уже естьctxв области видимости.cache.purge(...): импортировано изcloudflare:workers. Используйте это, когда нужно вызвать purge из кода, который не получает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.cache.purge(...) в большинстве примеров, потому что в этих примерах уже есть ctx в области видимости. Если предпочитаете форму импорта, замените cache.purge(...) : всё остальное остаётся без изменений.
Режимы очистки
purge() принимает либо purgeEverything: true самостоятельно, либо одно или оба из tags и pathPrefixes:
| Поле | Очистки | Область действия |
|---|---|---|
tags |
Каждый кешированный ответ, помеченный одним из указанных значений через Cache-Tag. |
За точку входа |
pathPrefixes |
Каждый кешированный ответ, путь запроса которого начинается с одного из указанных префиксов. | За точку входа |
purgeEverything |
Каждый кешированный ответ для entrypoint, который вызвал purge(). |
За точку входа |
purgeEverything применяется отдельно: сочетайте tags и pathPrefixes в одном вызове, если хотите, но не передавайте ни один из них вместе с purgeEverything.
Все три режима привязаны к области действия entrypoint который вызвал purge(). Очистка из PublicAPI не влияет на кешированные ответы, сохраненные AdminAPI, даже если у них совпадают имена тегов или префиксы путей. Чтобы сбросить кеш для всех entrypoint Worker, вызовите purge() из каждой точки входа.
Возвращённый промис разрешается в объект результата, который можно проверить, чтобы подтвердить успех или обработать ошибки. См. Возвращаемое значение.
Очистка кеша после записи с помощью вызова 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;Поля можно объединять в одном вызове. Например, purge({ tags: ["blog-posts"], pathPrefixes: ["/blog/"] }) очищает все, что соответствует либо тег или path-prefix: поля объединяются, а не пересекаются. Используйте этот вариант, когда одна логическая инвалидация затрагивает ответы, помеченные по нескольким схемам.
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 символа в длину, а ответ может содержать до 1000 тегов. Сопоставление тегов при очистке кэша выполняется без учёта регистра, поэтому Foo и foo очищают один и тот же набор ответов. Некорректные теги незаметно отбрасываются при сохранении: ответ все равно кешируется с оставшимися корректными тегами. См. Лимиты Cache Tag с полным списком.
Запустить очистку
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(). Тег с именем user-42 применённое к ответам в двух разных entrypoints, это не аннулируется одним purge({ tags: ["user-42"] }) вызов: он влияет только на ту точку входа, из которой был выполнен вызов. Если нужно сбросить один и тот же тег для нескольких точек входа, вызовите purge() из каждой точки входа, либо централизовать вызовы очистки кеша в общей точке входа, которая кеширует каждый ответ, который вам впоследствии нужно будет аннулировать.
Используйте иерархические теги
Чтобы аннулировать группы связанных ответов одним вызовом, присвойте каждому ответу несколько тегов, представляющих каждый уровень иерархии, к которому он принадлежит (иногда их называют «мягкими тегами»):
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/.
Ограничения на количество, длину и допустимые символы тегов см. в Лимиты Cache Tag.
Очистка кэша для конкретной версии
По умолчанию Workers Caching разбивает кэш по версиям Worker, поэтому каждый деплой уже начинается с холодного кеша, и очистка для конкретной версии не требуется. Этот раздел применяется только если вы включили cache.cross_version_cache чтобы совместно использовать кешированные ответы для разных версий. В этом случае ответ, записанный версией A, может продолжать отдаваться после развёртывания версии B, и может понадобиться очистить записи, созданные конкретной версией, например после отката. Для этого пометьте каждый ответ версией, которая его создала, и позже очистите записи с этой меткой.
Добавьте привязка метаданных версии в конфигурацию Wrangler:
{
"name": "my-worker",
"main": "src/index.ts",
// Set this to today's date
"compatibility_date": "2026-08-28",
"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-08-28"
[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 действует в пределах точки входа, из которой выполнен вызов очистки кеша. purge({ pathPrefixes: ["/blog/"] }) от PublicAPI не повлияет на закешированные ответы, сохранённые AdminAPI, даже если их пути также начинаются с /blog/.
Очистка одного URL
Отдельного режима «очистки по URL» не существует. Чтобы аннулировать один кешированный URL, передайте его путь в виде 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() используйте 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() использует ту же систему ограничения частоты запросов, что и API очистки зоны Cloudflare. Однако, поскольку Workers Caching привязан к Worker, а не к зоне, Workers Cache всегда использует Лимиты уровня Free описанного в Доступность и ограничения, независимо от тарифного плана вашего аккаунта или зоны. Если на очистку наложено ограничение по частоте запросов, success это false и errors содержит запись с описанием отклонения.