1---
2title: Workers Cache
3description: Serve responses from Cloudflare's edge cache before your Worker even runs — enable it with one yield, control it with standard headers, purge it by tag from inside a handler.
4sidebar:
5 order: 13
6---
7
8Workers Cache sits in front of a Worker: when it's enabled, Cloudflare
9checks the cache before invoking the Worker at all, and a hit is
10served straight from the edge. What gets cached is controlled by
11standard response headers: `Cache-Control` (including
12`stale-while-revalidate`), `Cache-Tag` for tag-based purging, and
13`Vary` for content negotiation.
14
15## Enable the cache on a Worker
16
17`yield*` `Cloudflare.cache()` in the Worker's Construction phase. It turns
18Workers Cache on at deploy time and returns the runtime client. There
19is no layer to provide:
20
21```typescript
22// src/worker.ts
23import * as Cloudflare from "alchemy/Cloudflare";
24import * as Effect from "effect/Effect";
25import { HttpServerRequest } from "effect/http/HttpServerRequest";
26import * as HttpServerResponse from "effect/http/HttpServerResponse";
27
28export default Cloudflare.Worker(
29 "CachedWorker",
30 { main: import.meta.url },
31 Effect.gen(function* () {
32 const { purge } = yield* Cloudflare.cache();
33
34 return {
35 fetch: Effect.gen(function* () {
36 const request = yield* HttpServerRequest;
37
38 if (request.url.startsWith("/invalidate")) {
39 yield* purge({ tags: ["products"] }).pipe(Effect.orDie);
40 return HttpServerResponse.text("purged");
41 }
42
43 return HttpServerResponse.text("hello", {
44 headers: {
45 "Cache-Control": "public, max-age=300",
46 "Cache-Tag": "products",
47 },
48 });
49 }),
50 };
51 }),
52);
53```
54
55Responses that carry a cacheable `Cache-Control` header are stored at
56the edge; for the next five minutes, repeat requests to the same URL
57never reach the handler.
58
59## Purge by tag
60
61The `purge` client drives the runtime `ctx.cache` API from inside a
62handler. Purge cached responses by the `Cache-Tag` values they were
63served with, by path prefix, or wholesale:
64
65```typescript
66yield* purge({ tags: ["products"] });
67```
68
69Failures surface as a typed `CachePurgeError`:
70
71```typescript
72const result = yield* purge({ tags: ["products"] }).pipe(
73 Effect.catchTag("CachePurgeError", (error) =>
74 Effect.succeed({ success: false, errors: [error.message] }),
75 ),
76);
77```
78
79## Share the cache across deploys
80
81By default the cache is scoped to a single Worker version, so every
82deploy starts cold. Opt into sharing cached responses across versions
83when your responses don't depend on the code that produced them:
84
85```typescript
86const { purge } = yield* Cloudflare.cache({ crossVersionCache: true });
87```
88
89`enabled: false` keeps the binding's purge client available while
90turning the read-through cache off, which is useful for toggling
91cache behavior per stage.
92
93## Enable the cache with the `cache` prop
94
95Workers whose `main` module exports a plain async `fetch` handler
96enable the cache with the `cache` prop instead:
97
98```typescript
99// alchemy.run.ts
100export const Worker = Cloudflare.Worker("Worker", {
101 main: "./src/worker.ts",
102 cache: { enabled: true, crossVersionCache: true },
103});
104```
105
106The handler shapes cache behavior the same way, through
107`Cache-Control`, `Cache-Tag`, and `Vary` headers on its responses.
108
109## Where next
110
111- [Workers](/cloudflare/compute/workers) — the two-phase Worker model
112 the cache fronts.
113- [cache API reference](/providers/cloudflare/workers/cache) — options
114 and purge types.