← Cloudflare Workers / workers
Workers Cache
Workers Cache позволяет Cloudflare возвращать кешированные HTTP ответы вашего Worker без выполнения его кода. Если входящий запрос совпадает с кешированным ответом, Cloudflare отдаёт ответ прямо из edge кеша, что снижает задержку и расход процессорного времени Workers.
Кеширование работает для любого fetch() вызов Worker: запросы конечных пользователей (запросы от браузеров и API-клиентов), запросы, отправленные через привязки к сервисам, и loopback fetch() вызовов между точками входа через ctx.exports. Вы управляете кешированием с помощью стандартных HTTP Cache-Control директивы в ваших ответах.
Кэш вашего Worker
Workers Cache кэш вашего Worker. Он принадлежит вашему Worker, управляется вашим Worker и доступен только ему.
Worker представляет собой сущность без зоны: он может быть привязан к любому количеству зоны, выполняется на workers.dev, либо вызываться полностью через service bindings, вообще не затрагивая зону. Кеш следует за Worker, а не за зоной, поэтому:
- Настройки кеширования зоны не применяются к Workers Caching. Cache Rules, Cache Response Rules, Page Rules, настройками уровня кэша, зоны по умолчанию cached-file-extensions список, и любое другое управление кешем на уровне зоны не влияет на кеш Worker.
- Worker полностью всё контролирует. Вы задаёте
Cache-Controlзаголовки в своих ответах, и Cloudflare учитывает их для каждого RFC 9111 ↗. Это исчерпывающий набор параметров конфигурации. - Кэш является общим для всех способов вызова Worker. Worker, привязанный к
api.example.com,api.example.net, и вызванный через service binding, отдает один и тот же кэшированный ответ всем трем, поскольку кэш формируется по пути запроса, entrypoint,ctx.props, и (по умолчанию) версией Worker, а не именем хоста. См. Ключи кеша.
Worker является поверхностью конфигурации
Worker представляет собой уже бесконечно настраиваемый. Вы можете изменять тело ответа, переписывать заголовки, ветвить логику по любому атрибуту запроса, обращаться к другим Workers через service bindings или ctx.exports, и объединять логику в рамках всей системы.
Workers Caching опирается именно на это. Вместо того чтобы вводить отдельный слой конфигурации для управления кешированием, он позволяет вашему Worker выражать это намерение напрямую, через Cache-Control заголовков, которые он возвращает, ctx.props которые он принимает, и программные операции очистки кеша, которые он выполняет. Всё, что вы, возможно, захотите настроить в отношении кеширования, можно настроить в коде:
- Нужен более длинный TTL для определённых путей? Разветвите логику по пути в Worker и задайте другой
max-age. - Нужно убрать параметр отслеживания из запроса перед кешированием? Перепишите URL или
ctx.propsв шлюзовом Worker перед диспетчеризацией. - Нужно разделение кеша по арендаторам? Укажите идентификатор арендатора в
ctx.props: то есть входит в ключ кеша. - Нужно обходить кеш для аутентифицированных пользователей? Верните
Cache-Control: private, либо полагаться на автоматический обход вызванныйSet-CookieиAuthorization.
Уже написанный вами Worker служит механизмом конфигурации. Workers Caching работает перед ним и учитывает любые заголовки, которые возвращает Worker.
Когда кэширование помогает
Кеширование хорошо подходит для Workers, которые:
- Выполняйте задачи с высокой нагрузкой на CPU, результат которых можно повторно использовать в разных запросах: генерация контента, рендеринг шаблонов, преобразование данных.
- Получаете данные от медленного источника или стороннего API и хотите скрыть эту задержку при последующих запросах.
- Используйте для работы сайта с серверным рендерингом или статической генерацией, где многие запросы возвращают одинаковый ответ.
Кеширование бесполезно для ответов, зависящих от пользователя и меняющихся при каждом запросе, а также для неидемпотентных операций (POST, PUT, DELETE), или ответы, которые каждый раз нужно вычислять заново.
Как это работает
При включенном кешировании Cloudflare проверяет кеш перед запуском Worker. При совпадении (hit) кешированный ответ возвращается напрямую. При отсутствии совпадения (miss) выполняется Worker, и если ответ можно кешировать согласно его Cache-Control заголовок, Cloudflare сохраняет его для следующего запроса.
flowchart LR
accTitle: Cache before a Worker request flow
accDescr: Request arrives at Cloudflare, cache is consulted before Worker execution.
Request["Request"] --> Cache{"Cache"}
Cache -- Hit --> Response["Cached response returned"]
Cache -- Miss --> Worker["Worker runs"]
Worker --> Store["Response stored in cache"]
Store --> Response2["Response returned"]
Tiered cache
Кеширование Workers многоуровневое по умолчанию. Cloudflare использует два уровня кеша для вашего Worker:
- Нижний уровень : кеш в дата-центре Cloudflare, ближайшем к пользователю (eyeball). У каждого дата-центра, получающего трафик для вашего Worker, есть собственный кеш нижнего уровня.
- Верхний уровень : меньший набор дата-центров, к которым обращается каждый нижний уровень при промахе кеша. Верхний уровень объединяет заполнение кеша по всей сети.
Запрос обслуживается из нижнего уровня кэша, если там происходит cache hit. Если происходит cache miss, нижний уровень обращается к верхнему уровню. Если верхний уровень тоже не находит ответ, срабатывает ваш Worker и генерирует ответ, который затем сохраняется в оба уровни на обратном пути, поэтому последующие запросы из любого дата-центра получают выгоду от этого.
flowchart LR
accTitle: Tiered cache for Workers
accDescr: A request hits the lower-tier cache first, then the upper-tier cache, then the Worker.
Request["Request"] --> Lower{"Lower-tier cache<br/>(near eyeball)"}
Lower -- Hit --> Response["Cached response returned"]
Lower -- Miss --> Upper{"Upper-tier cache"}
Upper -- Hit --> Lower
Upper -- Miss --> Worker["Worker runs"]
Worker --> Upper
Это та же топология, на которой работает Tiered Cache для зон, автоматически применяется к вашему Worker. Вы не настраиваете это, и многоуровневое кеширование работает независимо от того, использует ли ваш Worker Smart Placement.
Почему это важно: первый запрос для данного ключа кеша в любой точке земного шара заполняет верхний уровень. Каждый последующий запрос из любого дата-центра Cloudflare может быть обслужен с верхнего уровня без запуска вашего Worker, даже если нижний уровень в этой локации ещё ни разу не видел этот запрос. Коэффициент попаданий в кеш заметно выше, чем при использовании одного плоского уровня кеша.
Request Collapsing
Если на дата-центр Cloudflare одновременно поступает много запросов с одним и тем же ключом кэша, а ответ еще не закэширован, Cloudflare запускает ваш Worker один раз и передаёт полученный ответ всем ожидающим запросам. Это тот же самый объединение запросов механизм, который использует кеш зоны и который автоматически применяется к Workers Caching. Ожидающие запросы блокируются по каждому блокировка кеша пока первый запрос не приведет к ответу.
flowchart LR
accTitle: Cache request collapsing for Workers
accDescr: Many simultaneous requests for the same cache key produce one Worker invocation; all requests receive the same response.
R1["Request 1"] --> Lock
R2["Request 2"] --> Lock
R3["Request 3"] --> Lock
Rn["..."] --> Lock
Lock{"Cache lock<br/>(per cache key, per data center)"}
Lock -- "first request" --> Worker["Worker runs once"]
Worker --> Response["Response<br/>streamed to all<br/>waiting requests"]
Почему это важно: без объединения запросов внезапный всплеск трафика на новый URL вызывал бы Worker при каждом запросе, умножая расходы на процессорное время и нагрузку на любой бэкенд, к которому обращается Worker. При включенном объединении запросов такой всплеск по прежнему приводит только к одному вызову Worker.
Несколько моментов, которые следует учитывать:
- Схлопывание запросов выполняется отдельно для каждого ключа кэша в каждом дата-центре. Запросы, которые формируют разные ключи кеша, не объединяются между собой. Если два дата-центра одновременно не находят ответ в кеше, каждый из них по одному разу выполнит ваш Worker (на более высоком уровне иерархии кеша происходит дополнительная консолидация, подробнее см. Tiered cache).
- Потоковые ответы также сворачиваются. Ожидающие запросы присоединяются к уже выполняющемуся потоку ответа, поэтому получают тело по мере его формирования: им не нужно ждать полный ответ, прежде чем начнут возвращаться первые байты.
- Схлопывание запросов не применяется к некэшируемым ответам. Если ответ Worker нельзя закешировать (
BYPASS,DYNAMIC), каждый запрос получает отдельный вызов. Кеш объединяет только те запросы, ответ на которые кешу разрешено сохранять.
Это одно из главных отличий Workers Caching от Cache API : Cache API не объединяет параллельные запросы, поэтому всплеск трафика на новый URL вызывает Worker отдельно для каждого запроса.
Быстрый старт
Это краткое руководство поможет включить кеширование, развернуть приложение и увидеть работу кеша на практике.
1. Включите кеширование в конфигурации Wrangler
{
"name": "my-worker",
"main": "src/index.ts",
// Set this to today's date
"compatibility_date": "2026-08-28",
"cache": {
"enabled": true,
},
}name = "my-worker"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-08-28"
[cache]
enabled = true2. Возврат кешируемого ответа из вашего Worker
Используйте max-age чтобы управлять тем, как долго Cloudflare кеширует каждый ответ:
export default {
async fetch(request) {
const body = JSON.stringify({
timestamp: new Date().toISOString(),
random: Math.random(),
});
return new Response(body, {
headers: {
"Content-Type": "application/json",
// Cache for 1 hour; serve stale for up to 5 minutes while revalidating.
"Cache-Control": "public, max-age=3600, stale-while-revalidate=300",
},
});
},
};export default {
async fetch(request): Promise<Response> {
const body = JSON.stringify({
timestamp: new Date().toISOString(),
random: Math.random(),
});
return new Response(body, {
headers: {
"Content-Type": "application/json",
// Cache for 1 hour; serve stale for up to 5 minutes while revalidating.
"Cache-Control": "public, max-age=3600, stale-while-revalidate=300",
},
});
},
} satisfies ExportedHandler;3. Развёртывание и наблюдение за кешем
Разверните ваш Worker:
npx wrangler deployЗатем отправьте два запроса и посмотрите на Cf-Cache-Status заголовок ответа:
curl -I https://my-worker.example.workers.dev/HTTP/2 200
cache-control: public, max-age=3600, stale-while-revalidate=300
cf-cache-status: MISScurl -I https://my-worker.example.workers.dev/HTTP/2 200
cache-control: public, max-age=3600, stale-while-revalidate=300
cf-cache-status: HITВторой запрос получает кешированный ответ. timestamp и random значения в теле идентичны в обоих запросах, хотя Worker генерирует новые значения при каждом запуске. Это подтверждает, что второй запрос не выполнил ваш Worker.
Что кешируется
- HTTP-вызовы обработчика Worker
fetchобработчика подлежат кэшированию, включая запросы конечных пользователей и запросы через Service Bindingfetch()вызовы, а также loopbackfetch()вызовов черезctx.exports. - Только
GETиHEADзапросы кешируются. Остальные методы всегда вызывают ваш Worker.GETиHEADдля одного и того же URL используют одну запись кеша: см. Ключи кеша. - Только
fetch()вызовы проходят через кеш. Пользовательский Методы RPC наWorkerEntrypoint(например,ctx.exports.Backend.getUser(id)) полностью обходят кеш и всегда выполняют вызываемую функцию. Чтобы кешировать часть работы, оформите её какfetchобработчик в своей собственной точке входа. - Запросы на обновление до WebSocket обходят кеш. Есть
GETзапрос, содержащийUpgrade: websocketвсегда вызывает ваш Worker. - Другие типы вызовов:
scheduled(Cron Triggers),queueпотребителей, Workflows, Tail Workers, Durable Object вызовы, Email Workers : всегда выполняются без участия кеша. - Кешируемость определяется заголовками ответа, которые возвращает ваш Worker. Workers Caching следует семантике, определённой в RFC 9111 ↗, включая эвристическая свежесть ↗ для ответов, у которых нет
Cache-Control. См. Cache-Control для полного списка директив, которые учитывает Cloudflare. - стандартный для Cloudflare условия обхода кеша применяются. В частности, ответы с
Set-Cookieзаголовок, и запросы сAuthorizationзаголовок запускает автоматический обход кеша. - Preview URLs поддерживаются. Каждый предпросмотр кеширует данные независимо от рабочего развёртывания, поэтому проверка изменений, влияющих на кеш, в режиме предпросмотра никак не затрагивает закешированные ответы в продакшене.
- Workers for Platforms поддерживается. У каждого пользовательского Worker есть собственный кеш, изолированный от диспетчера и от других пользовательских Workers в пространстве имён.
Cf-Cache-Status заголовок ответа показывает, что произошло с каждым запросом.
Чаще всего встречаются значения HIT, MISS, EXPIRED, REVALIDATED,
UPDATING, STALE, а также BYPASS. См. Кэш Cloudflare
ответы для полного набора значений.
Согласование содержимого с использованием Vary
Кеширование Workers учитывает Vary ↗ заголовок ответа, как определено в RFC 9110 ↗ и RFC 9111 ↗. Когда ваш Worker возвращает Vary заголовок, Cloudflare сохраняет отдельный вариант кеша для каждой уникальной комбинации значений перечисленных заголовков запроса и возвращает сохранённый вариант только тогда, когда заголовки входящего запроса совпадают с теми, под которыми этот вариант был сохранён.
Это позволяет одному URL кешировать несколько представлений, например разные кодировки, типы контента или языки, без того чтобы Worker вручную согласовывал содержимое:
export default {
async fetch(request) {
const accept = request.headers.get("Accept") ?? "";
const wantsWebp = accept.includes("image/webp");
const body = wantsWebp ? await fetchWebpImage() : await fetchJpegImage();
return new Response(body, {
headers: {
"Content-Type": wantsWebp ? "image/webp" : "image/jpeg",
"Cache-Control": "public, max-age=3600",
// Cache a separate variant per distinct Accept header value.
Vary: "Accept",
},
});
},
};export default {
async fetch(request): Promise<Response> {
const accept = request.headers.get("Accept") ?? "";
const wantsWebp = accept.includes("image/webp");
const body = wantsWebp ? await fetchWebpImage() : await fetchJpegImage();
return new Response(body, {
headers: {
"Content-Type": wantsWebp ? "image/webp" : "image/jpeg",
"Cache-Control": "public, max-age=3600",
// Cache a separate variant per distinct Accept header value.
Vary: "Accept",
},
});
},
} satisfies ExportedHandler;Примечания:
Vary: *отключает кеширование ответа. Вариативность с подстановочным знаком невозможно детерминированно определить по заголовкам запроса, поэтому Cloudflare не сохраняет ответ.- Варианты используют одну общую запись кэша для целей очистки: выполнение очистки тег или префикс пути, соответствующий любому варианту, аннулирует все варианты этого URL. Поэтому все варианты одного URL должны использовать один и тот же
Cache-Tagзначения. Varyнесовместим с функциями преобразования изображений, которые уже создают собственные варианты (Polish, Image Resizing). Ответы, переписанные этими функциями, игнорируютVary.- Варианты сохраняются для каждого точного значения заголовка запроса. Клиенты, которые отправляют семантически эквивалентные, но текстуально разные значения, например
Accept-Encoding: gzip, brиAccept-Encoding: br, gzip: создают отдельные варианты. Если нужно уменьшить разрастание вариантов, приведите заголовки, которые видит ваш Worker, к единому виду (например, нормализовав их в шлюзовом Worker перед передачей запроса дальше).
Кеширование между Workers
Когда один Worker вызывает другой через привязка к сервису, вызываемой функции происходит обращение к кешу. Если для вызываемой функции включено кеширование и существует подходящий кешированный ответ, вызывающая сторона получает его без вызова самой функции.
flowchart LR
accTitle: Cache between Workers
accDescr: Worker A calls Worker B; Worker B's cache is consulted before Worker B runs.
Request["Request"] --> WorkerA["Worker A"]
WorkerA --> CacheB{"Worker B's cache"}
CacheB -- Hit --> WorkerA
CacheB -- Miss --> WorkerB["Worker B"]
WorkerB --> CacheB
Ключ кэша для вызовов через привязку сервиса включает ctx.props, поэтому разные вызывающие стороны с разным контекстом авторизации кешируются отдельно. Подробнее см. Ключи кеша.
При вызовах в пределах одной учётной записи вызывающий Worker может также настраивать кеширование для вызываемого объекта индивидуально для каждого запроса, задавая cf.cacheKey чтобы переопределить ключ кеша или cf.cacheControl чтобы передать Cache-Control директива.
Кеширование ответов Durable Object
Durable Objects никогда не кешируются напрямую через Workers Caching. Однако, поскольку Workers Caching работает перед любым entrypoint Worker, вы можете кешировать HTTP ответы Durable Object, обернув его в именованная точка входа Worker и кеширования entrypoint.
Точка входа обёртки перенаправляет запрос в Durable Object и задаёт Cache-Control к ответу, который он возвращает. Поскольку Workers Caching работает перед entrypoint, последующие запросы обслуживаются из кеша без повторного обращения к Durable Object.
Точка входа по умолчанию здесь представляет собой шлюз, который должен выполняться при каждом запросе, поэтому отключите на ней кеширование и включите его на CachedCounter (см. Кэширование для каждой точки входа):
{
"name": "my-worker",
"main": "src/index.ts",
// Set this to today's date
"compatibility_date": "2026-08-28",
"cache": { "enabled": true },
"exports": {
"default": { "type": "worker", "cache": { "enabled": false } },
"CachedCounter": { "type": "worker", "cache": { "enabled": true } },
},
}name = "my-worker"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-08-28"
[cache]
enabled = true
[exports.default]
type = "worker"
[exports.default.cache]
enabled = false
[exports.CachedCounter]
type = "worker"
[exports.CachedCounter.cache]
enabled = trueimport { WorkerEntrypoint } from "cloudflare:workers";
// Cached entrypoint. Requests to this entrypoint are served from cache
// when possible; on a miss, the Durable Object is invoked and its
// response is stored.
export class CachedCounter extends WorkerEntrypoint {
async fetch(request) {
const id = this.env.COUNTER.idFromName("global");
const stub = this.env.COUNTER.get(id);
const response = await stub.fetch(request);
// Attach cache headers. Clone into a new Response so the headers
// are mutable.
return new Response(response.body, {
status: response.status,
headers: {
...Object.fromEntries(response.headers),
"Cache-Control": "public, max-age=30",
},
});
}
}
// Default entrypoint. Delegates to the cached entrypoint via ctx.exports,
// which routes through the cache.
export default {
async fetch(request, env, ctx) {
return ctx.exports.CachedCounter.fetch(request);
},
};import { WorkerEntrypoint } from "cloudflare:workers";
interface Env {
COUNTER: DurableObjectNamespace;
}
// Cached entrypoint. Requests to this entrypoint are served from cache
// when possible; on a miss, the Durable Object is invoked and its
// response is stored.
export class CachedCounter extends WorkerEntrypoint<Env> {
async fetch(request: Request): Promise<Response> {
const id = this.env.COUNTER.idFromName("global");
const stub = this.env.COUNTER.get(id);
const response = await stub.fetch(request);
// Attach cache headers. Clone into a new Response so the headers
// are mutable.
return new Response(response.body, {
status: response.status,
headers: {
...Object.fromEntries(response.headers),
"Cache-Control": "public, max-age=30",
},
});
}
}
// Default entrypoint. Delegates to the cached entrypoint via ctx.exports,
// which routes through the cache.
export default {
async fetch(request, env, ctx): Promise<Response> {
return ctx.exports.CachedCounter.fetch(request);
},
} satisfies ExportedHandler<Env>;Подробнее о паттернах, сочетающих entrypoint шлюза с кешируемыми внутренними entrypoint, см. в Примеры.
Smart Placement и кэш
Smart Placement перемещает где выполняется ваш Worker когда он выполняется: как правило, ближе к медленному источнику или базе данных. Кеш при этом не перемещается. У Workers Caching всегда есть нижний уровень рядом с конечным пользователем и верхний уровень, агрегирующий сеть, именно так, как описано в Tiered cache выше, независимо от того, включен ли Smart Placement.
Перед тем как учитывается Smart Placement, всегда сначала проверяется кэш. А именно:
- Попадание в нижний уровень: ответ возвращается из дата-центра, ближайшего к конечному пользователю. Ваш Worker не выполняется. Smart Placement не задействуется.
- Промах на нижнем уровне, попадание на верхнем уровне: ответ возвращается с верхнего уровня. Ваш Worker не выполняется. Smart Placement не задействуется.
- На обоих уровнях отсутствует: Smart Placement направляет выполнение вашего Worker к целевому месту размещения (например, ближе к источнику). Итоговый ответ сохраняется в обоих уровнях кеша на обратном пути к eyeball.
Важно, что верхний уровень и целевое расположение Smart Placement являются независимыми локациями. Верхний уровень (upper tier) выбирается Cloudflare для агрегации заполнения кэша по всей сети; цель Smart Placement выбирается для минимизации задержки между вашим Worker и его бэкендом. Как правило, они находятся не в одном и том же дата-центре.
flowchart LR
accTitle: Tiered cache with Smart Placement across three locations
accDescr: The eyeball, the upper-tier cache, and the Smart Placement target are three independent locations. Requests traverse them in order on a full cache miss.
subgraph EyeballColo["Data center near eyeball"]
Request["Request"] --> Lower{"Lower-tier cache"}
end
subgraph UpperColo["Upper-tier data center"]
Upper{"Upper-tier cache"}
end
subgraph PlacedColo["Smart Placement target"]
Placed["Worker runs"]
Origin["Origin / backend"]
Placed <--> Origin
end
Lower -- Hit --> Response["Response"]
Lower -- Miss --> Upper
Upper -- Hit --> Lower
Upper -- Miss --> Placed
Placed --> Upper
При полном промахе кэша запрос проходит через три точки: дата-центр нижнего уровня рядом с пользователем (eyeball), дата-центр верхнего уровня и целевой сервер Smart Placement. Уровни кэширования берут на себя эту стоимость, поэтому медленный переход к целевому серверу размещения оплачивается только один раз для всей сети. Верхний уровень защищает целевой сервер размещения от промахов на каждом нижнем уровне.
Очистка кеша
Worker может в любой момент сбросить собственный кэш с помощью ctx.cache.purge(). Теги дают наибольшую гибкость: помечайте ответы с помощью Cache-Tag при их возврате, а позже очистить эти теги:
export default {
async fetch(request, env, ctx) {
await ctx.cache.purge({ tags: ["blog-posts"] });
return new Response("Purged", { status: 200 });
},
};export default {
async fetch(request, env, ctx): Promise<Response> {
await ctx.cache.purge({ tags: ["blog-posts"] });
return new Response("Purged", { status: 200 });
},
} satisfies ExportedHandler;Вы также можете import cache от cloudflare:workers и вызвать cache.purge({...}) если у вас нет ctx в области видимости, например из вспомогательного модуля. Обо всех режимах и шаблонах очистки см. в Очистка кеша.
Цены
Workers Cache не имеет отдельной тарификации. При включении Workers Cache все запросы к вашему Worker оплачиваются по стандартной Частота запросов Workers : та же ставка за запрос, что и для любого другого запроса к вашему Worker, независимо от того, приходит ли ответ из кеша или от вашего Worker. Дополнительная плата сверх стандартной ставки за запрос не взимается. Время CPU оплачивается только тогда, когда ваш Worker выполняется : попадания в кеш не расходуют процессорное время.
| Тип запроса | Плата за запрос | Тарификация времени CPU |
|---|---|---|
Cache HIT (Worker не выполняется) |
Стандартный тариф | Не тарифицируется |
Cache MISS (Worker выполняется) |
Стандартный тариф | Тарифицируется |
Cache BYPASS (Worker выполняется) |
Стандартный тариф | Тарифицируется |
| Запрос статического ресурса | Стандартный тариф | Не тарифицируется |
| Вызов Worker из Worker | Стандартный тариф | Тарифицируется, если Worker выполняется |
Пример см. в Пример цены: Worker с кешированием.