INTEGRITY Dokumentace

Cache keys

Každá odpověď uložená v mezipaměti se ukládá pod cache key. Když dorazí požadavek, Cloudflare pro něj vypočítá cache key a vyhledá ho: při zásahu (hit) se vrátí uložená odpověď, při netrefení (miss) se spustí váš Worker a jeho odpověď se pod tímto klíčem uloží pro příště.

Dva požadavky, které vytvoří stejný klíč mezipaměti, sdílí stejnou uloženou odpověď. Dva požadavky, které vytvoří odlišné klíče mezipaměti, získají nezávislé záznamy v mezipaměti.

Tato stránka vysvětluje, co Workers Caching vkládá do cache klíče, proč tam každá součást je, a jak nad tím uvažovat při návrhu vašeho Workeru.

Co tvoří cache key

Workers Caching klíčuje odpovědi podle:

Jako opatření proti otravě cache klíč také obsahuje:

Tyto tři body obvykle nejsou něco, nad čím byste museli přemýšlet. Některé frameworky interpretují hlavičky method-override a URL-rewrite jako přepsání efektivní metody nebo URL požadavku, což může vést k otrava cache pokud se dva požadavky liší jen v těchto hlavičkách, ale vedou k podstatně odlišným odpovědím. Zahrnutí těchto hlaviček do cache key zajistí, že poškozený záznam ovlivní jen požadavky se stejnou závadnou hlavičkou.

Požadavky, které se liší pouze v hlavičkách požadavku, jež nejsou součástí klíče cache (například User-Agent, Accept-Language, Cookie, nebo Authorization) vrací stejnou odpověď z mezipaměti. To je obvykle to, co chcete: nechcete, aby si každý řetězec User Agent nebo každá jazyková preference vytvářely samostatný záznam v mezipaměti. Pokud vyjednávání obsahu skutečně potřebujete, nastavte Vary na odpovědi, nebo ji zpracujte přímo ve svém Workeru a vytvořte kanonickou odpověď pro každou URL.

Za zmínku stojí, že klíč cache ne patří:

Při spuštění nelze zjistit přesný klíč mezipaměti, který Cloudflare pro daný požadavek vypočítala. Hlavními signály pro pochopení chování cache jsou Cf-Cache-Status hlavičku odpovědi a informace o zásahu do mezipaměti pro jednotlivá volání v Přehledový panel pozorovatelnosti Workers. Viz Kontrola cache klíče.

Cache patří Workeru, nikoli doméně

Worker je entita bez zóny. Lze ho vyvolat několika různými způsoby:

Workers Caching považuje všechny tyto případy za stejný Worker a používá pro ně jednu sdílenou mezipaměť. Klíč mezipaměti neobsahuje hostitele, takže požadavek na /api/users/42 trefí se do stejného záznamu v mezipaměti bez ohledu na to, zda přišel přes api.example.com, api.example.net, vazbu na službu nebo workers.dev URL.

Toto chování obvykle chcete. Odpovědi Workeru jsou funkcí jeho kódu a vstupů, nikoli toho, přes kterou doménu požadavek přišel. Uložení odpovědi do cache jednou a její vracení pro každou vstupní cestu proto maximalizuje cache hit rate, aniž by to ovlivnilo správnost.

Pokud pro stejnou cestu na různých hostname skutečně potřebujete odlišné odpovědi v mezipaměti, například u white-label tenantů, kde tenant-a.example.com/index a tenant-b.example.com/index musí generovat odlišný obsah: klíč mezipaměti to za vás automaticky nezajistí. Místo toho rozlište jednotlivé tenanty ve svém gateway Workeru a předejte identifikátor tenanta pomocí ctx.props, který je část klíče cache.

Zneplatňování cache napříč nasazeními

Ve výchozím nastavení aktuálně volaná verze Workeru je část klíče cache. Každá nasazená verze má vlastní cache, takže:

Jde o výchozí chování, protože se nad ním nejsnáze uvažuje. Kompromisem je, že míra úspěšnosti cache se po každém nasazení vynuluje, the first requests to a new version are misses while its cache fills. This is the most common reason a Worker's cache hit rate drops right after a deploy.

Sdílení cache mezi verzemi

Pokud nasazujete často a vaše odpovědi se mezi nasazeními mění jen zřídka, je zahazování zahřáté mezipaměti při každém nasazení plýtváním. Nastavte cache.cross_version_cache na true abyste odstranili verzi z klíče mezipaměti a sdíleli mezipaměťové odpovědi napříč verzemi. Odpověď zapsaná verzí A se pak stále doručuje i po nasazení verze B, dokud nevypršela její TTL.

Toto maximalizuje cache hit rate na úkor pomalejších rolloutů: protože nasazení už neinvaliduje cache, změna, která upraví obsah odpovědi, se u již cachovaných záznamů projeví až po jejich vypršení nebo po jejich vyčištění (purge). Pokud máte cross_version_cache zapnuto a potřebujete, aby se nasazení projevilo okamžitě, použijte jeden z níže uvedených dvou nástrojů.

Označte odpovědi verzí, při rollbacku značku purgujte

Pokud chcete mít podrobnou kontrolu, označte každou odpověď uloženou v mezipaměti verzí Workeru, která ji vytvořila. Následné vyčištění podle tohoto tagu verze pak odstraní všechny záznamy zapsané danou verzí, aniž by to ovlivnilo odpovědi z jiných verzí v mezipaměti.

Toto využívá vazba metadat verze pro čtení aktuálního ID verze v době požadavku a přidá ho jako Cache-Tag hodnota. Viz Vymazání specifické pro verzi pro úplný vzor s kódem.

Toto je nejlepší volba, pokud máte povoleno cross_version_cache a možná budete potřebovat vrátit konkrétní verzi zpět, aniž byste přišli o obsah uložený v mezipaměti z funkčních verzí.

Vymazání všeho po nasazení

Jednodušší přístup: po každém nasazení zavolejte z CI malý endpoint Workeru, který volá ctx.cache.purge({ purgeEverything: true }). Další požadavek po purge znovu naplní cache podle verze Workeru, která je v danou chvíli aktivní.

Je to hrubší řešení, ale nevyžaduje žádnou logiku uvnitř Workeru. Použijte jej, pokud máte povoleno cross_version_cache ale přesto chcete, aby konkrétní nasazení zneplatňovala cache. U výchozí cache podle verze začíná každé nasazení stejně s prázdnou cache, takže to není potřeba.

Bezpečnost pro více nájemců pomocí ctx.props

Když je váš Worker vyvolán přes service binding nebo RPC, volajícího ctx.props je součástí klíče mezipaměti. Dva volající, kteří vyvolají váš Worker s odlišnými ctx.props získat samostatné položky v mezipaměti, one caller can never receive another caller's cached response.

Toto je mechanismus, díky kterému je ukládání do cache bezpečné pro multi-tenant Workers volané přes service binding. Pokud používáte ctx.props nese autorizační kontext pro jednotlivého volajícího: ID uživatele, ID tenanta, organizaci, roli, ukládání do mezipaměti je proto ve výchozím nastavení bezpečné. Odpovědi, které logicky patří jednomu volajícímu, nemohou přes mezipaměť uniknout k jinému.

src/backend.js
import { WorkerEntrypoint } from "cloudflare:workers";

export default class Backend extends WorkerEntrypoint {
	async fetch(request) {
		// ctx.props.userId is set by the caller (for example, an auth gateway).
		// Because it is part of the cache key, User A and User B requesting the
		// same URL get separate cache entries — there is no way for one to
		// see the other's response.
		const { userId } = this.ctx.props;
		const data = { userId, timestamp: Date.now() };

		return new Response(JSON.stringify(data), {
			headers: {
				"Content-Type": "application/json",
				"Cache-Control": "public, max-age=300",
			},
		});
	}
}
src/backend.ts
import { WorkerEntrypoint } from "cloudflare:workers";

interface Props {
	userId: string;
}

export default class Backend extends WorkerEntrypoint<Env, Props> {
	async fetch(request: Request): Promise<Response> {
		// ctx.props.userId is set by the caller (for example, an auth gateway).
		// Because it is part of the cache key, User A and User B requesting the
		// same URL get separate cache entries — there is no way for one to
		// see the other's response.
		const { userId } = this.ctx.props;
		const data = { userId, timestamp: Date.now() };

		return new Response(JSON.stringify(data), {
			headers: {
				"Content-Type": "application/json",
				"Cache-Control": "public, max-age=300",
			},
		});
	}
}

URL Service bindingu

Volání přes Service binding si zaslouží zvláštní pozornost: URL, kterou zadáváte, neznamená to, co byste čekali.

Když zavoláte service binding pomocí fetch(), hostname v URL je pouze zástupný. Požadavek se směruje přes binding, nikoli přes DNS, hostname se nikdy nepřekládá. A protože host není součástí klíče cache (jak je popsáno v Cache patří Workeru, nikoli doméně), zástupný symbol nemá vliv ani na ukládání do mezipaměti. Pouze cesta (a query string) přispívají ke klíči mezipaměti, spolu s cílovým entrypointem a ctx.props:

src/gateway.js
export default {
	async fetch(request, env, ctx) {
		// "internal" here is just a placeholder — it is not routed anywhere
		// and is not part of the cache key.
		//
		// What identifies this cached response is:
		//   - the BACKEND entrypoint
		//   - the path "/api/users/42"
		//   - whatever ctx.props the gateway passes along
		return env.BACKEND.fetch("http://internal/api/users/42");
	},
};
src/gateway.ts
interface Env {
	BACKEND: Fetcher;
}

export default {
	async fetch(request, env, ctx): Promise<Response> {
		// "internal" here is just a placeholder — it is not routed anywhere
		// and is not part of the cache key.
		//
		// What identifies this cached response is:
		//   - the BACKEND entrypoint
		//   - the path "/api/users/42"
		//   - whatever ctx.props the gateway passes along
		return env.BACKEND.fetch("http://internal/api/users/42");
	},
} satisfies ExportedHandler<Env>;

Pokud chcete, aby se odpovědi uložené v mezipaměti lišily podle jednotlivých volajících, měňte ctx.props. Pokud chcete, aby se lišily podle jednotlivých požadavků, rozlište je podle cesty nebo query stringu. Rozlišování podle hostname nemá žádný efekt.

Kontrola cache klíče

Při spuštění vám o chování cache dávají přehled dva signály:

  1. Cf-Cache-Status hlavička odpovědi. Nejčastěji se setkáte s hodnotami HIT, MISS, EXPIRED, REVALIDATED, UPDATING, STALE, a BYPASS. HIT znamená, že Cloudflare vrátil odpověď z cache, aniž by spustil váš Worker. MISS znamená, že váš Worker proběhl a odpověď byla uložena. UPDATING znamená, že odpověď v cache byla zastaralá a váš Worker běžel na pozadí, aby ji obnovil. BYPASS znamená, že ukládání do cache bylo pro tento požadavek vypnuté. Více informací naleznete v Odpovědi mezipaměti Cloudflare pro úplnou sadu hodnot.

  2. Zásahy do mezipaměti v Přehledový panel pozorovatelnosti Workers. Každé volání zobrazuje, zda bylo obslouženo z cache, takže můžete filtrovat a agregovat chování cache hit napříč provozem vašeho Workeru.

Cloudflare v současnosti nezobrazuje přímo samotné složení cache key. Pokud dva požadavky, u kterých jste očekávali sdílenou odpověď z cache, ji nesdílejí, musíte zjistit, která část klíče se lišila od komponent uvedených v Co tvoří cache key. Podrobný návod k řešení běžných problémů s mezipamětí a jejich diagnostice najdete v Ladění.

Custom Cache Keys

Ve výchozím nastavení tvoří cesta a query string URL adresy požadavku URL složku cache key. Když jeden entrypoint vyvolá jiný cachovaný entrypoint prostřednictvím ctx.exports loopback, volající entrypoint může tuto komponentu přepsat nastavením cf.cacheKey na požadavku.

V následujícím příkladu Backend vstupní bod je ten, který se ukládá do cache. Výchozí vstupní bod na něj přeposílá požadavky přes ctx.exports, čímž si zvolíte samotný klíč cache:

src/index.js
import { WorkerEntrypoint } from "cloudflare:workers";

// Cached entrypoint. Requests routed here through ctx.exports are served
// from cache when possible.
export class Backend extends WorkerEntrypoint {
	async fetch(request) {
		return new Response("Hello from the backend", {
			headers: {
				"Content-Type": "text/html",
				"Cache-Control": "public, max-age=3600",
			},
		});
	}
}

// Gateway entrypoint. Calls the cached Backend entrypoint via ctx.exports,
// which routes through the cache, and chooses the cache key for the call.
export default {
	async fetch(request, env, ctx) {
		const url = new URL(request.url);

		// Strip a tracking parameter so that requests differing only by
		// `utm_source` resolve to the same cached entry.
		url.searchParams.delete("utm_source");

		return ctx.exports.Backend.fetch(request, {
			cf: { cacheKey: url.pathname + url.search },
		});
	},
};
src/index.ts
import { WorkerEntrypoint } from "cloudflare:workers";

// Cached entrypoint. Requests routed here through ctx.exports are served
// from cache when possible.
export class Backend extends WorkerEntrypoint<Env> {
	async fetch(request: Request): Promise<Response> {
		return new Response("Hello from the backend", {
			headers: {
				"Content-Type": "text/html",
				"Cache-Control": "public, max-age=3600",
			},
		});
	}
}

// Gateway entrypoint. Calls the cached Backend entrypoint via ctx.exports,
// which routes through the cache, and chooses the cache key for the call.
export default {
	async fetch(request, env, ctx): Promise<Response> {
		const url = new URL(request.url);

		// Strip a tracking parameter so that requests differing only by
		// `utm_source` resolve to the same cached entry.
		url.searchParams.delete("utm_source");

		return ctx.exports.Backend.fetch(request, {
			cf: { cacheKey: url.pathname + url.search },
		});
	},
} satisfies ExportedHandler<Env>;

Vlastní cache key nahradí cestu a řetězec dotazu v cache key. Vše ostatní popsané v Co tvoří cache key stále platí:

Nastavte cf.cacheKey na prázdný řetězec, nebo jej vynechte, čímž se použije výchozí klíč odvozený z URL.

V tomto vzoru je výchozí entrypoint bránou, která by měla běžet při každém požadavku, proto na ní zakažte caching a ponechte jej zapnutý pro Backend (viz Ukládání do mezipaměti pro jednotlivé entrypointy):

{
	"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 } },
		"Backend": { "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.Backend]
type = "worker"

  [exports.Backend.cache]
  enabled = true

Co můžete dělat s vlastním cache key

Pro izolaci podle volajícího nadále používejte ctx.props místo toho, aby se identita volajícího kódovala do klíče cache, ctx.props je automaticky součástí klíče a nelze to obejít.

Vlastní klíče platí pouze pro volání v rámci stejného účtu

cf.cacheKey se respektuje pouze tehdy, když volání zůstává v rámci vašeho účtu. Cloudflare odstraní cf objekt pokaždé, když požadavek překročí hranici účtu, například service binding na Worker vlastněný jiným účtem. V takovém případě se vlastní klíč ignoruje a klíč mezipaměti se vrátí k URL požadavku, takže volající v jednom účtu nikdy nemůže ovlivnit (ani prozkoumat) mezipaměť Workeru v jiném účtu.

To také znamená cf.cacheKey nemá žádný vliv na požadavky koncových uživatelů. cf objekt u příchozího požadavku z prohlížeče nebo API klienta vyplňuje Cloudflare, nikoli klient, takže si klient nemůže nastavit vlastní klíč mezipaměti.