← Cloudflare Workers / workers / cache
Конфигурация
Кеширование Workers настраивается для каждого Worker отдельно, в файле конфигурации Wrangler. При включении кеширование применяется к каждому fetch() вызов: запросы конечных пользователей, service binding fetch() вызовы, а также loopback fetch() вызовов между точками входа через ctx.exports : если только вы отключить его для конкретной точки входа. Пользовательский Методы RPC обходят кеш.
Это кэш вашего Worker : настраивается через код вашего Worker и файл Wrangler. Worker полностью управляет своим кешем через:
-
cache.enabledфлаг в конфигурации Wrangler, который включает или отключает кеширование. Вы можете переопределить его на точку входа и управлять различия в поведении между версиями. -
Cache-Control(иcdn-cache-control,cloudflare-cdn-cache-control) которые ваш Worker задаёт в своих ответах, согласно RFC 9111 ↗. - Необязательный
Cache-Tagзаголовок ответа для пакетной очистки, иctx.cache.purge()для программного аннулирования.
Это и есть весь набор доступных настроек.
Включите кэширование
Добавьте cache блок в конфигурацию 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 = trueНастройка cache.enabled к true приводит к тому, что Cloudflare проверяет кэш перед вызовом вашего Worker при каждом HTTP запросе. Это поведение по умолчанию для каждой точки входа; вы можете переопределить его для конкретной точки входа с помощью exports.
cache блок принимает два поля: 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-08-28",
"cache": {
"enabled": false,
},
}name = "my-worker"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-08-28"
[cache]
enabled = falseОтключение кеширования не удаляет уже закешированные ответы: это лишь останавливает обращение Cloudflare к кешу и его заполнение при последующих запросах. Если вы позже снова включите кеширование, записи, у которых ещё не истёк TTL, снова станут доступны. Если нужно, чтобы кешированные ответы перестали отдаваться немедленно, очистить кэш после отключения.
Кэширование для каждой точки входа
cache.enabled задает значение по умолчанию для всего Worker, но Worker может предоставлять несколько entrypoints : экспорт по умолчанию и любое количество именованных WorkerEntrypoint классы, и для каждого из них можно независимо включать или отключать кэширование. Используйте exports map с ключами по имени точки входа и "default" ссылаясь на экспорт по умолчанию:
{
"name": "my-worker",
"main": "src/index.ts",
// Set this to today's date
"compatibility_date": "2026-08-28",
"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-08-28"
[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> } }. Отдельный для каждого entrypoint cache.enabled переопределяет параметр верхнего уровня cache.enabled для этой точки входа; точки входа, которые вы не укажете, наследуют значение верхнего уровня. Вы также можете включить кеширование для одной точки входа без cache блок, указав только эту точку входа.
Это позволяет вам включать и отключать отдельные точки входа без изменения кода вашего Worker:
- Исключить entrypoint чтобы он выполнялся при каждом запросе. Это естественный выбор для точки входа шлюза или маршрутизатора, которая выполняет аутентификацию, нормализацию или диспетчеризацию и сама никогда не должна отдаваться из кеша. Это рекомендуемый способ создания паттерн шлюза: отключите кеширование на точке входа шлюза и включите его на внутренней точке входа, через которую шлюз выполняет вызов
ctx.exports. - Включить entrypoint чтобы кешировать только те точки входа, которые возвращают ответы, пригодные для повторного использования, оставляя остальную часть Worker без кеширования.
Версионированные развёртывания
cache конфигурация входит в состав версии вашего Worker:
- Каждая версия, загруженная с помощью
wrangler deployилиwrangler versions uploadзахватывает всё, чтоcache.enabledзначение указано в его конфигурации Wrangler. - Откат к предыдущей версии также откатывает
cacheнастройка, привязанная к этой версии. - Вы можете использовать постепенные развёртывания чтобы включить кэширование для части трафика перед применением его к 100% трафика. Во время постепенного развертывания с версии, где кэширование отключено, на версию, где оно включено, трафик, направленный на старую версию, обрабатывается без кэширования, как и раньше, а трафик, направленный на новую версию, обращается к кэшу и заполняет его. По умолчанию версия Worker входит в состав ключа кэша, поэтому обе версии заполняют независимые записи кэша и не обслуживают ответы друг друга: см. Кеширование между версиями.
Кеширование между версиями
По умолчанию Версия Worker входит в состав ключа кеша. У каждой развёрнутой версии свой изолированный кэш, поэтому новое развёртывание начинается с пустого кэша и никогда не отдаёт ответы, записанные предыдущей версией. Такое поведение принято по умолчанию, поскольку его проще всего понять: новое развёртывание вступает в силу немедленно, и вы никогда не получаете ответ, созданный вытесненной версией.
Компромисс заключается в том, что коэффициент попаданий в кеш сбрасывается при каждом развёртывании. Поскольку новая версия не может использовать закешированные ответы предыдущей версии, первые запросы после развёртывания оказываются промахами, пока кеш новой версии не наполнится. Это самая частая причина падения cache hit rate у Worker сразу после развёртывания.
Если вы хотите максимизировать долю попаданий в кэш и готовы мириться с более медленным раскатыванием изменений, влияющих на кэш, укажите cross_version_cache к true. Закешированные ответы затем становятся общими для всех версий: ответ, записанный одной версией, может быть отдан более поздней версией, пока не истёк его TTL:
{
"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,
},
}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Опытным пользователям, которые часто деплоят и чьи ответы почти не меняются между деплоями, стоит рассмотреть включение cross_version_cache : это позволяет не терять прогретый кеш при каждом деплое. Плата за это в том, что деплой больше не сбрасывает кеш: после изменения, которое меняет содержимое ответа, старые закешированные ответы продолжают отдаваться, пока не истечёт их срок действия или пока вы очистка их, а во время постепенное развёртывание обе версии используют общий кеш. Если развертывание должно вступить в силу немедленно с cross_version_cache включено, очистите кеш после деплоя либо помечайте ответы версией: см. Аннулирование кэша между развёртываниями.
cross_version_cache действует только при включённом кешировании. Он применяется ко всем точкам входа с включённым кешем.
Конфигурация для конкретного окружения
cache блок можно задать на верхнем уровне и переопределить для каждого окружение. Типичный подход: включать кэширование в production, когда вы уверены в его безопасности, и оставлять staging без кэша для упрощения отладки:
{
"name": "my-worker",
"main": "src/index.ts",
// Set this to today's date
"compatibility_date": "2026-08-28",
"cache": {
"enabled": false,
},
"env": {
"production": {
"cache": {
"enabled": true,
},
},
},
}name = "my-worker"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-08-28"
[cache]
enabled = false
[env.production.cache]
enabled = trueСемантика Cache-Control
При включенном кешировании ваш Worker выступает источником (origin) для кеша Cloudflare. Стандартные HTTP Cache-Control директивы в ответе, который возвращает ваш Worker, определяют, кеширует ли Cloudflare этот ответ и на какой срок. Полный список директив и их взаимодействие см. в Cache-Control.
Настройте окно свежести с помощью max-age
Используйте max-age чтобы управлять тем, как долго ответ считается актуальным:
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>`;
}Если браузерам и edge-серверам нужно кешировать данные на разное время, используйте cdn-cache-control (или cloudflare-cdn-cache-control) для директивы, действующей только на edge, и сохраните Cache-Control для того, что видят браузеры. См. Приоритет заголовков ниже.
Используйте stale-while-revalidate для обновлений с низкой задержкой
Когда кэшированный ответ устаревает, stale-while-revalidate позволяет Cloudflare сразу возвращать устаревший ответ и обновлять его в фоновом режиме:
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;Выберите значения TTL и stale-while-revalidate
Высокий процент попаданий в кеш и высокая свежесть данных противоречат друг другу. Фоновая ревалидация скрывает задержку обновления кеша, однако Worker всё равно запускается при каждой ревалидации: это не бесплатно.
Два распространённых паттерна:
- В основном статический контент с небольшим допуском на устаревание. Используйте короткий
max-age(например, 60 секунд) и более длительныйstale-while-revalidateокне (например, 3600 секунд). Большинство запросовHITs; отдельные запросы иногда запускают фоновое обновление. - «Всегда обслуживать из кеша» для эндпоинтов с высокой нагрузкой. Используйте
max-age=0, stale-while-revalidate=<large>. Каждый запрос сразу возвращает ранее кэшированный ответ и запускает фоновое обновление. Ваш Worker выполняется по одному разу на каждый запрос для повторной проверки, поэтому расход CPU почти такой же, как при выполнении Worker на каждый запрос. Актуальность данных падает вместе со снижением частоты запросов: если долго не поступает ни одного запроса, следующий запрос увидит устаревшее содержимое.
Отдавайте устаревший контент при ошибке с stale-if-error
stale-if-error позволяет Cloudflare возвращать ранее закешированный ответ, если Worker завершается с ошибкой при обновлении просроченной записи кеша: например, если он выбрасывает исключение, превышает время ожидания или возвращает 5xx ответ. Это защищает клиентов от временных сбоев 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-if-error потому что отдавать устаревшие данные нечего: в этом случае ошибки Worker передаются клиентам напрямую.
Приоритет заголовков
Если присутствует несколько заголовков кэша, побеждает наиболее специфичный:
cloudflare-cdn-cache-control: специфично для Cloudflare, имеет наивысший приоритет. Обрабатывается Cloudflare и удаляется из ответа перед отправкой клиентам.cdn-cache-control: стандартный заголовок для директив, предназначенных только для CDN. Cloudflare учитывает его и передаёт нижестоящим CDN.Cache-Control: стандартный HTTP-заголовок. Cloudflare учитывает его и передаёт клиентам.
Используйте cloudflare-cdn-cache-control когда нужен более длинный edge TTL, чем тот, что вы показываете браузерам, не передавая эту директиву дальше по цепочке.
Переопределить Cache-Control от вызывающего Worker
Обычно вызываемая сторона сама определяет, как кешировать ответы, задавая Cache-Control для них. Когда одна точка входа вызывает другую кешируемую точку входа через ctx.exports loopback, вызов точка входа может вместо этого предоставлять Cache-Control директиву для этого вызова, задав cf.cacheControl к запросу.
Здесь Backend точка входа не возвращает Cache-Control собственную; точка входа по умолчанию определяет политику кеширования при вызове Backend через ctx.exports:
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 в другом аккаунте. Эта директива также не действует на запросы конечных пользователей (eyeball requests), поскольку cf объект во входящем запросе заполняется Cloudflare, а не клиентом.
Заголовки ответа
Cf-Cache-Status
Каждый ответ содержит Cf-Cache-Status заголовок, указывающий, что произошло с этим запросом. Чаще всего вы будете видеть значения HIT, MISS, EXPIRED, REVALIDATED, UPDATING, STALE, а также BYPASS. Полный список значений и их описание см. в Кеширование ответов Cloudflare.
Cache-Tag
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 Tag для полного списка. Наиболее распространённые ограничения, которые следует учитывать:
- Значения тегов должны быть печатаемые символы ASCII (
0x21-0x7E) без пробелов, без Unicode, без управляющих символов. - Каждый тег составляет не более 1024 символа в длину.
- Ответ может содержать до 1000 тегов для целей очистки кеша.
- Сопоставление тегов в момент очистки кэша является без учёта регистра.
Fooиfooочищают один и тот же набор ответов.
Недопустимые теги (слишком длинные, содержащие пробелы или символы не из набора ASCII) при кэшировании молча отбрасываются. Ответ всё равно кэшируется с оставшимися допустимыми тегами, но узнать, какие теги были отброшены, невозможно. Если это важно, проверяйте теги в Worker перед тем, как их возвращать.
Условия автоматического обхода кеша
Кеширование Workers наследует стандартные правила обхода кеша. Наиболее частые причины:
- Ответ включает
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) рассматриваются как временные ошибки и никогда не кешируются.
Range запросы
Workers Caching обслуживает Range запросы из закешированного полного ответа: вашему Worker не придётся самостоятельно реализовывать нарезку по диапазонам байтов (byte-range slicing).
Когда клиент отправляет Range запрос, Cloudflare удаляет Range заголовок перед вызовом вашего Worker и запрашивает у вашего Worker полное тело. Ваш Worker возвращает обычный 200 ответ с Cache-Control заголовка (как и для любого другого запроса), Cloudflare сохраняет этот полный ответ, а затем вырезает запрошенный диапазон байт и возвращает его клиенту как 206 Partial Content ответ (или 416 Range Not Satisfiable если диапазон недействителен). Последующие Range запросы к одному и тому же URL полностью обслуживаются из закешированной записи: ваш Worker не вызывается, и Cf-Cache-Status это HIT.
Например, GET с Range: bytes=0-9 при холодном кеше дает MISS на входе (ваш Worker выполняется и возвращает полное тело), а затем возвращает 206 с первыми 10 байтами. Последующий GET Range: bytes=10-19 для одного и того же URL является HIT и возвращает эти 10 байт из кеша, не вызывая ваш Worker.
Если ваш Worker возвращает 206 собственный ответ, например, потому что вы реализовали Range обработку внутри Worker, Cloudflare считает такой ответ некэшируемым и не сохраняет его. Верните полный 200 и позвольте Workers Caching самостоятельно обрабатывать разбиение диапазонов.
Vary
Когда ваш Worker возвращает Vary заголовок ответа, Cloudflare сохраняет отдельный кешированный вариант для каждой уникальной комбинации значений перечисленных заголовков запроса и возвращает только тот вариант, чьи сохраненные значения совпадают со значениями входящего запроса. Это реализует RFC 9110 ↗ и вычисление cache-key в RFC 9111 ↗. Вводную информацию с примерами кода см. в Согласование содержимого с использованием Vary.
Как Vary обрабатывается для Workers Caching:
- Учитываются все имена заголовков. Любое имя заголовка, указанное вашим Worker в
Varyучаствует в ключе варианта. Списка разрешений не предусмотрено. - Значения сравниваются буквально. Cloudflare не нормализует перечисленные заголовки запроса перед формированием ключа кеша.
Accept-Encoding: gzip, brиAccept-Encoding: br, gzipсоздают два разных варианта, даже если они семантически идентичны. Если вам нужно объединить эквивалентные значения в один вариант, нормализуйте заголовки, которые видит ваш Worker, в шлюзовом Worker перед передачей запроса, либо приводите их к каноническому виду внутри Worker, который задаётVary. Vary: *отключает кеширование. Подстановочное значение Vary невозможно однозначно определить по заголовкам запроса, поэтому ответ считается непригодным для кеширования иCf-Cache-StatusэтоBYPASS.- У вариантов общий идентификатор очистки. Очистка по тегу или префиксу пути сбрасывает сразу все варианты URL. Поэтому все варианты должны использовать один и тот же
Cache-Tagзначения. Присвоение разных тегов разным вариантам приводит к несогласованным очисткам. - Функции преобразования изображений имеют приоритет. Ответы, созданные Polish или Image Resizing, уже содержат собственные варианты, и
Varyдля этих ответов игнорируется.
Accept-Encoding и Content-Encoding
Worker сам управляет согласованием содержимого. Что бы ни Content-Encoding ваш Worker устанавливает в ответе, Cloudflare сохраняет и отдаёт в последующих запросах.
Если вашему Worker нужно возвращать разным клиентам разные кодировки, у вас есть два варианта:
- Выберите одну каноническую кодировку внутри вашего Worker. Примите решение на основе
Accept-Encodingзаголовок запроса, закодировать тело один раз и вернуть единственное представление. Последующие запросы к этому URL попадают в ту же запись кеша независимо от того, что они принимают. Это даёт самый высокий процент попаданий в кеш, но требует, чтобы вы сами решали, какую кодировку отдавать каким клиентам. - Vary по
Accept-Encoding. Верните другойContent-Encodingна запрос и задайтеVary: Accept-Encoding. Cloudflare хранит один вариант для каждого отдельногоAccept-Encodingзначение, которое видел Worker. Поскольку сравнение выполняется дословно, клиенты, отправляющие семантически эквивалентные значения в разном порядке или с разными коэффициентами качества, создают отдельные варианты. Чтобы разрастание кэша не выходило из-под контроля, нормализуйтеAccept-Encoding(например, в шлюзовом Worker) до того, как будет сгенерирован ответ.