INTEGRITY Документация

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, а не за зоной, поэтому:

Worker является поверхностью конфигурации

Worker представляет собой уже бесконечно настраиваемый. Вы можете изменять тело ответа, переписывать заголовки, ветвить логику по любому атрибуту запроса, обращаться к другим Workers через service bindings или ctx.exports, и объединять логику в рамках всей системы.

Workers Caching опирается именно на это. Вместо того чтобы вводить отдельный слой конфигурации для управления кешированием, он позволяет вашему Worker выражать это намерение напрямую, через Cache-Control заголовков, которые он возвращает, ctx.props которые он принимает, и программные операции очистки кеша, которые он выполняет. Всё, что вы, возможно, захотите настроить в отношении кеширования, можно настроить в коде:

Уже написанный вами Worker служит механизмом конфигурации. Workers Caching работает перед ним и учитывает любые заголовки, которые возвращает Worker.

Когда кэширование помогает

Кеширование хорошо подходит для Workers, которые:

Кеширование бесполезно для ответов, зависящих от пользователя и меняющихся при каждом запросе, а также для неидемпотентных операций (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:

Запрос обслуживается из нижнего уровня кэша, если там происходит 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.

Несколько моментов, которые следует учитывать:

Это одно из главных отличий 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 = true

2. Возврат кешируемого ответа из вашего Worker

Используйте max-age чтобы управлять тем, как долго Cloudflare кеширует каждый ответ:

src/index.js
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",
			},
		});
	},
};
src/index.ts
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: MISS
curl -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.

Что кешируется

Cf-Cache-Status заголовок ответа показывает, что произошло с каждым запросом. Чаще всего встречаются значения HIT, MISS, EXPIRED, REVALIDATED, UPDATING, STALE, а также BYPASS. См. Кэш Cloudflare ответы для полного набора значений.

Согласование содержимого с использованием Vary

Кеширование Workers учитывает Vary заголовок ответа, как определено в RFC 9110 и RFC 9111. Когда ваш Worker возвращает Vary заголовок, Cloudflare сохраняет отдельный вариант кеша для каждой уникальной комбинации значений перечисленных заголовков запроса и возвращает сохранённый вариант только тогда, когда заголовки входящего запроса совпадают с теми, под которыми этот вариант был сохранён.

Это позволяет одному URL кешировать несколько представлений, например разные кодировки, типы контента или языки, без того чтобы Worker вручную согласовывал содержимое:

src/index.js
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",
			},
		});
	},
};
src/index.ts
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;

Примечания:

Кеширование между 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 = true
src/index.js
import { 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);
	},
};
src/index.ts
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, всегда сначала проверяется кэш. А именно:

Важно, что верхний уровень и целевое расположение 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 при их возврате, а позже очистить эти теги:

src/index.js
export default {
	async fetch(request, env, ctx) {
		await ctx.cache.purge({ tags: ["blog-posts"] });
		return new Response("Purged", { status: 200 });
	},
};
src/index.ts
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 с кешированием.

Следующие шаги