← Cloudflare Workers / workers / cache
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:
- cílový entrypoint, which specific pojmenovaný entrypoint Workeru přijala požadavek.
defaultexport a exportovaná třída jsou odlišné vstupní body a nesdílejí cache, ani když vytvářejí identické odpovědi. - cesta a query string URL požadavku. Na pořadí parametrů dotazu záleží:
?a=1&b=2a?b=2&a=1jsou různé klíče cache. Záleží i na koncovém lomítku. - Verze Workeru, ve výchozím nastavení. Každá nasazená verze má vlastní cache, takže nové nasazení neposkytuje odpovědi uložené předchozí verzí. Vypnout to můžete pomocí
cache.cross_version_cachepro sdílení uložených odpovědí mezi verzemi. Více informací najdete v Zneplatňování cache napříč nasazeními. - U vyvolání
ctx.props, když je Worker vyvolán prostřednictvím service bindingu nebo RPC. Viz Bezpečnost pro více nájemců pomocíctx.props.
Jako opatření proti otravě cache klíč také obsahuje:
-
x-http-method-override,x-http-method, ax-method-overridehlavičky požadavku. -
x-forwarded-host,x-host,x-forwarded-scheme(pokud jeho hodnota neníhttpnebohttps),x-original-url,x-rewrite-url, aforwardedhlavičky požadavku. - Hodnota
Cloudflare-Workers-Version-Keyhlavičku požadavku. Tuto hlavičku Cloudflare automaticky nenastavuje: má význam pouze tehdy, když se ji volající (například upstream Worker nebo proxy) rozhodne přidat, aby mezipaměť dále explicitně rozdělil. Toto je nezávislé na automatickém klíčování podle verze popsaném výše, které řídícache.cross_version_cache.
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ří:
- Metoda HTTP.
GETaHEADpožadavky na stejnou URL adresu sdílejí jednu položku v mezipaměti.HEADpožadavek lze obsloužit zGETnaplnění (Cloudflare vrátí cachované hlavičky bez těla). V opačném směruHEADpožadavek se v případě studené mezipaměti převede naGETinterně, aby se veškerý asset načetl a uložil, takže následujícíGETpak zasáhne položku, kteráHEADvyplněno. (POST,PUT,PATCH, aDELETEse nikdy neukládají do mezipaměti, takže se u nich tato otázka vůbec neřeší.) - Hostitel požadavku. Mezipaměť Workeru je klíčovaná podle cesty a query stringu, nikoli podle celé URL. Viz Cache patří Workeru, nikoli doméně.
- Tělo požadavku. Protože pouze
GETaHEADlze ukládat do mezipaměti, což je málokdy relevantní, přesto to stojí za zmínku, pokud váš Worker čterequest.bodyna metodě, kterou lze ukládat do mezipaměti, tělo nerozděluje mezipaměť na oddíly.
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:
- Přímo na
workers.devsubdoména. - Prostřednictvím trasa na jakékoli zóně, kterou spravujete.
- Prostřednictvím vlastní doména : a stejný Worker můžete připojit k mnoha vlastním doménám.
- Prostřednictvím service binding z jiného Workeru, s libovolným zástupným hostname v URL.
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:
- Nové nasazení začíná se studenou mezipamětí a nikdy neposkytuje odpovědi zapsané předchozí verzí.
- Změny ovlivňující mezipaměť se projeví okamžitě po zprovoznění nové verze, není tedy potřeba nic vymazávat, aby se přestal zobrazovat starý obsah.
- Během postupné nasazení, stará a nová verze plní nezávislé cache, takže část provozu na nové verzi nikdy nedostane odpovědi ze staré verze.
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.
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",
},
});
}
}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:
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");
},
};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:
-
Cf-Cache-Statushlavička odpovědi. Nejčastěji se setkáte s hodnotamiHIT,MISS,EXPIRED,REVALIDATED,UPDATING,STALE, aBYPASS.HITznamená, že Cloudflare vrátil odpověď z cache, aniž by spustil váš Worker.MISSznamená, že váš Worker proběhl a odpověď byla uložena.UPDATINGznamená, že odpověď v cache byla zastaralá a váš Worker běžel na pozadí, aby ji obnovil.BYPASSznamená, ž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. -
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:
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 },
});
},
};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í:
- Cílový vstupní bod a volajícího
ctx.propszůstávají součástí klíče. Vlastní klíč mezipaměti nemůže přesahovat přes entrypointy ani přesctx.props, takže izolace mezi nájemci popsané výše platí i tehdy, když si volající zvolí vlastní klíče. Vlastní klíč vždy adresuje pouze položky v rámci vlastního cache namespace volané funkce. - Dva požadavky s různé adresy URL, ale stejný
cf.cacheKeyse překládají na stejnou položku v mezipaměti. Tímto způsobem sloučíte několik URL adres do jedné uložené odpovědi. - Dva požadavky s stejnou URL, ale jiné
cf.cacheKeyse překládají na samostatné položky v mezipaměti.
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 = trueCo můžete dělat s vlastním cache key
- Ignorujte části adresy URL. Odstraňte sledovací parametry (
utm_source,gclid), nebo řetězec dotazu úplně vynechat, aby varianty, které odpověď nemění, sdílely jeden záznam v cache. - Použijte jako klíč něco jiného než URL. Klíč sestavte z hodnoty, které váš gateway Worker důvěřuje, například z normalizovaného identifikátoru prostředku, aby se několik ekvivalentních URL adres namapovalo na jednu položku.
- Cache si rozdělte sami. Ke klíči připojte rozlišující hodnotu (například verzi obsahu), aby se vynutily samostatné položky pro požadavky, které by se jinak shodovaly.
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.