Sign inConnect your agent
infochanged

Workers - Workers Cache — mark cached responses stale with invalidate()

Announced
Tags
Workers
Severity
info, set by reef/rss-rule-v1
Source
https://developers.cloudflare.com/changelog/post/2026-09-29-workers-cache-invalidate/

<p><a href="https://developers.cloudflare.com/workers/cache/">Workers Cache</a> now supports <code>invalidate()</code>, the soft counterpart of <code>purge()</code>. <code>purge()</code> deletes matching cached responses, so the next request is a cache miss. <code>invalidate()</code> keeps them but marks them stale, so the cache revalidates them with your Worker instead.</p> <p>To revalidate a response, the cache sends your Worker a conditional request built from the validators stored with it — for example, <code>If-None-Match</code> carrying the cached <code>ETag</code>. If your Worker answers <code>304 Not Modified</code>, the cache keeps the stored body. If your Worker answers with a full <code>200</code> response, that response replaces the cached one.</p> <p><code>invalidate()</code> accepts the same options as <code>purge()</code>: <code>tags</code>, <code>pathPrefixes</code>, or <code>purgeEverything</code>. It follows the same per-entrypoint scoping and resolves to the same result object. Call it as <code>ctx.cache.invalidate()</code>, or import <code>cache</code> from <code>cloudflare:workers</code> and call <code>cache.invalidate()</code>.</p> <p>Use <code>invalidate()</code> when one call covers many cached responses but only some of them changed. Your Worker needs to emit <code>ETag</code> or <code>Last-Modified</code> and answer matching conditional requests with <code>304</code>. Each unchanged response then costs a validator check instead of a full regeneration:</p> <div><div data-nb-tabs data-nb-sync-key="workersExamples" class><div class="relative flex border-b border-border" role="tablist" data-nb-tabs-list><span class="bg-primary pointer-events-none absolute -bottom-px h-0.5 rounded-t-sm transition-[left,width] duration-200 ease-out" data-nb-tabs-indicator aria-hidden="true"></span></div><div class="mt-3"><div role="tabpanel" data-nb-tabs-content data-nb-tab-label="JavaScript" class><figure class="nb-code-figure nb-code-figure-titled" data-nb-lang="js"><figcaption class="nb-code-title"><span class="nb-code-title-name">src/index.js</span><span class="nb-code-title-lang">js</span></figcaption><pre class="astro-code astro-code-themes github-light github-dark nb-shiki-c6xiwz" tabindex="0" data-language="js" data-nb-lang="js"><code><span class="line"><span class="nb-shiki-1itgoe">export</span><span class="nb-shiki-1itgoe"> default</span><span class="nb-shiki-140thh"> {</span></span> <span class="line"><span class="nb-shiki-1itgoe"> async</span><span class="nb-shiki-1t8gfj"> fetch</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-1jdh33">request</span><span class="nb-shiki-140thh">, </span><span class="nb-shiki-1jdh33">env</span><span class="nb-shiki-140thh">, </span><span class="nb-shiki-1jdh33">ctx</span><span class="nb-shiki-140thh">) {</span></span> <span class="line"><span class="nb-shiki-1itgoe"> if</span><span class="nb-shiki-140thh"> (request.method </span><span class="nb-shiki-1itgoe">===</span><span class="nb-shiki-mdbnqw"> "POST"</span><span class="nb-shiki-140thh">) {</span></span> <span class="line"><span class="nb-shiki-21nrsd"> // Write the updated catalog, then mark every cached product page stale.</span></span> <span class="line"><span class="nb-shiki-1itgoe"> await</span><span class="nb-shiki-1t8gfj"> syncCatalog</span><span class="nb-shiki-140thh">(env, </span><span class="nb-shiki-1itgoe">await</span><span class="nb-shiki-140thh"> request.</span><span class="nb-shiki-1t8gfj">json</span><span class="nb-shiki-140thh">());</span></span> <span class="line"><span class="nb-shiki-1itgoe"> await</span><span class="nb-shiki-140thh"> ctx.cache.</span><span class="nb-shiki-1t8gfj">invalidate</span><span class="nb-shiki-140thh">({ tags: [</span><span class="nb-shiki-mdbnqw">"products"</span><span class="nb-shiki-140thh">] });</span></span> <span class="line"><span class="nb-shiki-1itgoe"> return</span><span class="nb-shiki-1itgoe"> new</span><span class="nb-shiki-1t8gfj"> Response</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-mdbnqw">"Synced"</span><span class="nb-shiki-140thh">);</span></span> <span class="line"><span class="nb-shiki-140thh"> }</span></span> <span class="line"></span> <span class="line"><span class="nb-shiki-1itgoe"> const</span><span class="nb-shiki-dzsirb"> product</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-1itgoe"> await</span><span class="nb-shiki-1t8gfj"> getProduct</span><span class="nb-shiki-140thh">(env, request);</span></span> <span class="line"><span class="nb-shiki-1itgoe"> const</span><span class="nb-shiki-dzsirb"> etag</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-mdbnqw"> "${</span><span class="nb-shiki-140thh">product</span><span class="nb-shiki-mdbnqw">.</span><span class="nb-shiki-140thh">revision</span><span class="nb-shiki-mdbnqw">}"</span><span class="nb-shiki-140thh">;</span></span> <span class="line"><span class="nb-shiki-1itgoe"> const</span><span class="nb-shiki-dzsirb"> headers</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-140thh"> {</span></span> <span class="line"><span class="nb-shiki-mdbnqw"> "Cache-Control"</span><span class="nb-shiki-140thh">: </span><span class="nb-shiki-mdbnqw">"public, max-age=86400"</span><span class="nb-shiki-140thh">,</span></span> <span class="line"><span class="nb-shiki-mdbnqw"> "Cache-Tag"</span><span class="nb-shiki-140thh">: </span><span class="nb-shiki-mdbnqw">"products"</span><span class="nb-shiki-140thh">,</span></span> <span class="line"><span class="nb-shiki-140thh"> ETag: etag,</span></span> <span class="line"><span class="nb-shiki-140thh"> };</span></span> <span class="line"></span> <span class="line"><span class="nb-shiki-21nrsd"> // The product has not changed since it was cached. Answer 304, and the</span></span> <span class="line"><span class="nb-shiki-21nrsd"> // cache keeps the body it already has.</span></span> <span class="line"><span class="nb-shiki-1itgoe"> if</span><span class="nb-shiki-140thh"> (request.headers.</span><span class="nb-shiki-1t8gfj">get</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-mdbnqw">"If-None-Match"</span><span class="nb-shiki-140thh">) </span><span class="nb-shiki-1itgoe">===</span><span class="nb-shiki-140thh"> etag) {</span></span> <span class="line"><span class="nb-shiki-1itgoe"> return</span><span class="nb-shiki-1itgoe"> new</span><span class="nb-shiki-1t8gfj"> Response</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-dzsirb">null</span><span class="nb-shiki-140thh">, { status: </span><span class="nb-shiki-dzsirb">304</span><span class="nb-shiki-140thh">, headers });</span></span> <span class="line"><span class="nb-shiki-140thh"> }</span></span> <span class="line"></span> <span class="line"><span class="nb-shiki-1itgoe"> return</span><span class="nb-shiki-1itgoe"> new</span><span class="nb-shiki-1t8gfj"> Response</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-1t8gfj">renderProductPage</span><span class="nb-shiki-140thh">(product), { headers });</span></span> <span class="line"><span class="nb-shiki-140thh"> },</span></span> <span class="line"><span class="nb-shiki-140thh">};</span></span></code></pre></figure></div><div role="tabpanel" data-nb-tabs-content data-nb-tab-label="TypeScript" class><figure class="nb-code-figure nb-code-figure-titled" data-nb-lang="ts"><figcaption class="nb-code-title"><span class="nb-code-title-name">src/index.ts</span><span class="nb-code-title-lang">ts</span></figcaption><pre class="astro-code astro-code-themes github-light github-dark nb-shiki-c6xiwz" tabindex="0" data-language="ts" data-nb-lang="ts"><code><span class="line"><span class="nb-shiki-1itgoe">export</span><span class="nb-shiki-1itgoe"> default</span><span class="nb-shiki-140thh"> {</span></span> <span class="line"><span class="nb-shiki-1itgoe"> async</span><span class="nb-shiki-1t8gfj"> fetch</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-1jdh33">request</span><span class="nb-shiki-140thh">, </span><span class="nb-shiki-1jdh33">env</span><span class="nb-shiki-140thh">, </span><span class="nb-shiki-1jdh33">ctx</span><span class="nb-shiki-140thh">)</span><span class="nb-shiki-1itgoe">:</span><span class="nb-shiki-1t8gfj"> Promise</span><span class="nb-shiki-140thh">&#x3C;</span><span class="nb-shiki-1t8gfj">Response</span><span class="nb-shiki-140thh">> {</span></span> <span class="line"><span class="nb-shiki-1itgoe"> if</span><span class="nb-shiki-140thh"> (request.method </span><span class="nb-shiki-1itgoe">===</span><span class="nb-shiki-mdbnqw"> "POST"</span><span class="nb-shiki-140thh">) {</span></span> <span class="line"><span class="nb-shiki-21nrsd"> // Write the updated catalog, then mark every cached product page stale.</span></span> <span class="line"><span class="nb-shiki-1itgoe"> await</span><span class="nb-shiki-1t8gfj"> syncCatalog</span><span class="nb-shiki-140thh">(env, </span><span class="nb-shiki-1itgoe">await</span><span class="nb-shiki-140thh"> request.</span><span class="nb-shiki-1t8gfj">json</span><span class="nb-shiki-140thh">());</span></span> <span class="line"><span class="nb-shiki-1itgoe"> await</span><span class="nb-shiki-140thh"> ctx.cache.</span><span class="nb-shiki-1t8gfj">invalidate</span><span class="nb-shiki-140thh">({ tags: [</span><span class="nb-shiki-mdbnqw">"products"</span><span class="nb-shiki-140thh">] });</span></span> <span class="line"><span class="nb-shiki-1itgoe"> return</span><span class="nb-shiki-1itgoe"> new</span><span class="nb-shiki-1t8gfj"> Response</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-mdbnqw">"Synced"</span><span class="nb-shiki-140thh">);</span></span> <span class="line"><span class="nb-shiki-140thh"> }</span></span> <span class="line"></span> <span class="line"><span class="nb-shiki-1itgoe"> const</span><span class="nb-shiki-dzsirb"> product</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-1itgoe"> await</span><span class="nb-shiki-1t8gfj"> getProduct</span><span class="nb-shiki-140thh">(env, request);</span></span> <span class="line"><span class="nb-shiki-1itgoe"> const</span><span class="nb-shiki-dzsirb"> etag</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-mdbnqw"> "${</span><span class="nb-shiki-140thh">product</span><span class="nb-shiki-mdbnqw">.</span><span class="nb-shiki-140thh">revision</span><span class="nb-shiki-mdbnqw">}"</span><span class="nb-shiki-140thh">;</span></span> <span class="line"><span class="nb-shiki-1itgoe"> const</span><span class="nb-shiki-dzsirb"> headers</span><span class="nb-shiki-1itgoe"> =</span><span class="nb-shiki-140thh"> {</span></span> <span class="line"><span class="nb-shiki-mdbnqw"> "Cache-Control"</span><span class="nb-shiki-140thh">: </span><span class="nb-shiki-mdbnqw">"public, max-age=86400"</span><span class="nb-shiki-140thh">,</span></span> <span class="line"><span class="nb-shiki-mdbnqw"> "Cache-Tag"</span><span class="nb-shiki-140thh">: </span><span class="nb-shiki-mdbnqw">"products"</span><span class="nb-shiki-140thh">,</span></span> <span class="line"><span class="nb-shiki-140thh"> ETag: etag,</span></span> <span class="line"><span class="nb-shiki-140thh"> };</span></span> <span class="line"></span> <span class="line"><span class="nb-shiki-21nrsd"> // The product has not changed since it was cached. Answer 304, and the</span></span> <span class="line"><span class="nb-shiki-21nrsd"> // cache keeps the body it already has.</span></span> <span class="line"><span class="nb-shiki-1itgoe"> if</span><span class="nb-shiki-140thh"> (request.headers.</span><span class="nb-shiki-1t8gfj">get</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-mdbnqw">"If-None-Match"</span><span class="nb-shiki-140thh">) </span><span class="nb-shiki-1itgoe">===</span><span class="nb-shiki-140thh"> etag) {</span></span> <span class="line"><span class="nb-shiki-1itgoe"> return</span><span class="nb-shiki-1itgoe"> new</span><span class="nb-shiki-1t8gfj"> Response</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-dzsirb">null</span><span class="nb-shiki-140thh">, { status: </span><span class="nb-shiki-dzsirb">304</span><span class="nb-shiki-140thh">, headers });</span></span> <span class="line"><span class="nb-shiki-140thh"> }</span></span> <span class="line"></span> <span class="line"><span class="nb-shiki-1itgoe"> return</span><span class="nb-shiki-1itgoe"> new</span><span class="nb-shiki-1t8gfj"> Response</span><span class="nb-shiki-140thh">(</span><span class="nb-shiki-1t8gfj">renderProductPage</span><span class="nb-shiki-140thh">(product), { headers });</span></span> <span class="line"><span class="nb-shiki-140thh"> },</span></span> <span class="line"><span class="nb-shiki-140thh">} </span><span class="nb-shiki-1itgoe">satisfies</span><span class="nb-shiki-1t8gfj"> ExportedHandler</span><span class="nb-shiki-140thh">&#x3C;</span><span class="nb-shiki-1t8gfj">Env</span><span class="nb-shiki-140thh">>;</span></span></code></pre></figure></div></div></div><script type="module" src="https://developers.cloudflare.com/home/runner/work/cloudflare-docs/cloudflare-docs/src/components/ui/tabs/Tabs.astro?astro&type=script&index=0&lang.ts"></script></div> <p>For more information, refer to <a href="https://developers.cloudflare.com/workers/cache/purge/#invalidate-cached-responses">Invalidate cached responses</a>.</p>

The text above is the source's. reef added the severity, the dates it could read from the text, and the endpoints and scopes named in code.