How to Set Up a Cache for Your Worker in Front of It
How HTTP Caching Headers Actually Work (And Why They Matter More Than Ever)
If you've spent any time optimizing web applications, you've encountered HTTP caching headers. They're deceptively simple-looking strings in an HTTP response, yet they control one of the most powerful performance mechanisms available to developers. With Cloudflare's recent announcement of Workers Cache — a regionally tiered cache that sits directly in front of Worker entrypoints, configured entirely through standard HTTP headers — it's worth taking a deep step back and understanding exactly how these headers work, what they mean, and how to use them correctly.
Because here's the thing: most developers use caching headers reactively, copying configurations from Stack Overflow or framework defaults without fully understanding what they're doing. That leads to bugs that are notoriously hard to diagnose: stale content served to users, cache poisoning vulnerabilities, or worse — no caching at all when you thought there was.
Let's fix that.
What HTTP Caching Headers Are Actually Doing
At their core, HTTP caching headers are instructions. They tell browsers, CDN edge nodes, reverse proxies, and now Worker-level caches exactly how to store, validate, and serve responses. The two most important directives live in the Cache-Control header, which replaced the older Expires header in HTTP/1.1.
Here's a minimal example:
HTTP/1.1 200 OK
Cache-Control: max-age=3600, public
Content-Type: application/json
This tells any cache in the chain: "You can store this response and serve it for up to 3600 seconds (one hour) without checking back with the origin."
But that single line hides a lot of complexity. Let's break down the most important directives.
max-age vs s-maxage
max-age applies to all caches — the browser's private cache and any shared caches (CDNs, proxies). s-maxage applies only to shared caches and overrides max-age for those layers.
Cache-Control: max-age=60, s-maxage=86400
This configuration tells the browser to cache for 60 seconds, but allows your CDN or edge cache to hold onto the response for 24 hours. This is extremely useful for API responses where you want relatively fresh data in the browser but want to protect your origin from thundering herd scenarios.
public vs private
public explicitly permits shared caches to store the response, even if the request included authentication headers. private restricts caching to the end-user's browser only — no CDN, no proxy should store it.
Cache-Control: private, max-age=300
Use private for personalized responses: user dashboards, account pages, anything containing user-specific data. Failing to mark these as private can result in one user's data being served to another — a serious security issue.
no-cache vs no-store
These two are frequently confused:
no-cachedoes not mean "don't cache." It means "always revalidate with the origin before serving from cache." The response can still be stored.no-storemeans "never store this response anywhere, ever."
# Revalidate every time, but can store the response
Cache-Control: no-cache
# Never store this response
Cache-Control: no-store
For sensitive data like banking information or medical records, no-store is the right choice. For dynamic content that changes frequently but doesn't contain sensitive information, no-cache with proper ETag or Last-Modified headers gives you the best of both worlds.
Validation: ETags and Last-Modified
Storing a cached response is only half the picture. The other half is revalidation — checking whether a stored response is still fresh without downloading the entire response body again.
ETags
An ETag is an opaque identifier (typically a hash of the content) that the server generates for a specific version of a resource:
HTTP/1.1 200 OK
Cache-Control: no-cache
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Content-Type: text/html
When the cache revalidates, it sends the ETag back to the server in an If-None-Match header:
GET /index.html HTTP/1.1
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
If the content hasn't changed, the server responds with 304 Not Modified and no body — saving bandwidth. If it has changed, the server returns the new content with a new ETag.
Last-Modified
Last-Modified works similarly but uses timestamps instead of content hashes:
HTTP/1.1 200 OK
Last-Modified: Tue, 15 Oct 2024 12:00:00 GMT
Cache-Control: max-age=0, must-revalidate
The revalidation request uses If-Modified-Since:
GET /styles.css HTTP/1.1
If-Modified-Since: Tue, 15 Oct 2024 12:00:00 GMT
ETags are generally preferred over Last-Modified because they're more precise — timestamps can have one-second granularity issues, and some systems modify file timestamps without changing content.
Stale-While-Revalidate: The Best Pattern You're Probably Not Using
One of the most powerful (and underused) caching directives is stale-while-revalidate. It allows a cache to serve a stale response immediately while simultaneously fetching a fresh one in the background:
Cache-Control: max-age=60, stale-while-revalidate=3600
This configuration means: "Serve fresh for 60 seconds. After that, serve stale for up to 3600 seconds while revalidating in the background."
The practical effect is that users almost never wait for a cache miss. The first user after the TTL expires gets the stale response instantly, and the cache refreshes in the background. The next user gets the fresh response.
This pattern is particularly powerful at the edge — exactly the scenario Cloudflare's Workers Cache is designed for. When your Worker sits behind a regional cache configured with stale-while-revalidate, you get low-latency responses for the vast majority of requests with origin protection built in.
Similarly, stale-if-error lets you serve stale content if the origin returns an error:
Cache-Control: max-age=3600, stale-if-error=86400
This is essentially a free resilience mechanism — if your origin goes down for up to 24 hours, users keep getting (slightly stale) responses instead of error pages.
Vary: The Header That Controls Cache Segmentation
The Vary header tells caches that the response may differ based on specific request headers. This is critical for content negotiation:
HTTP/1.1 200 OK
Vary: Accept-Encoding
Cache-Control: public, max-age=86400
This tells caches to store separate versions of the response for different Accept-Encoding values — so gzip-compressed and uncompressed versions are cached separately.
Common Vary use cases:
# Different responses for different content types
Vary: Accept
# Different responses for different languages
Vary: Accept-Language
# Different responses for mobile vs desktop (use carefully)
Vary: User-Agent
Warning: Vary: User-Agent is almost always a bad idea. There are thousands of distinct User-Agent strings, which effectively defeats caching entirely. If you need device-specific responses, use separate URLs or cookies instead.
Cache-Control in Practice: Real-World Configurations
Here are some concrete configurations for common scenarios:
Static Assets with Content Hashing
If your build process generates hashed filenames (like main.a3f9c2.js), you can cache forever:
Cache-Control: public, max-age=31536000, immutable
The immutable directive tells the browser not to revalidate even on explicit refresh — a significant performance win for returning visitors.
HTML Pages
HTML pages typically need more careful handling since they reference other assets:
Cache-Control: public, max-age=0, must-revalidate
Or with stale-while-revalidate for better perceived performance:
Cache-Control: public, max-age=300, stale-while-revalidate=3600
API Responses
For public API responses that change infrequently:
Cache-Control: public, max-age=60, s-maxage=300, stale-while-revalidate=600
For authenticated API responses:
Cache-Control: private, no-cache
ETag: "abc123"
Webhook Endpoints and Mutation Endpoints
These should never be cached:
Cache-Control: no-store
How Edge Caches Differ from Browser Caches
Understanding that there are multiple caches in the chain is crucial. A typical request passes through:
- Browser cache — local to the user's device, private
- CDN/Edge cache — shared, geographically distributed
- Origin server — your application
Each layer can have different TTLs via s-maxage. The edge cache layer is where the most interesting optimization happens, because it protects your origin from traffic spikes while staying geographically close to users.
With Cloudflare's Workers Cache specifically, there's now a cache layer that sits in front of your Worker code itself. This means even the compute cost of running your Worker can be avoided for cacheable responses — the cache returns the response before your Worker code even executes.
This changes how you think about cache headers in your Worker responses. Previously, you might have set aggressive cache headers to help downstream CDN layers. Now, those same headers directly control whether your Worker runs at all for subsequent requests.
You can inspect what cache headers your server is actually sending (versus what you think it's sending) using the Cache Inspector tool — it shows the raw HTTP headers and helps identify misconfigurations like missing Cache-Control headers or conflicting directives.
Security Considerations for Caching
Caching headers aren't just a performance concern — they have real security implications.
Never Cache Sensitive Responses
Any response containing authentication tokens, personal data, or session information must use Cache-Control: private, no-store. A misconfigured shared cache serving one user's authenticated response to another user is a serious data breach.
Cache Poisoning
Cache poisoning attacks work by tricking a cache into storing a malicious response and serving it to other users. Mitigations include:
- Validating and normalizing request inputs before they influence cache keys
- Using
Varyheaders appropriately to segment caches - Avoiding caching responses that include unvalidated user input
Security Headers Should Not Be Cached Aggressively
Headers like Content-Security-Policy, X-Frame-Options, and Strict-Transport-Security should be consistent. If you're caching responses, make sure security headers are included in the cached response and aren't accidentally stripped by your caching layer.
Speaking of security headers — the Vulnerability Scanner can check whether your security headers are properly configured and identify common misconfigurations that caching might expose.
Debugging Cache Behavior
Caching bugs are infamously difficult to diagnose because the symptoms (stale content, unexpected responses) often look like application bugs. Here are practical debugging approaches:
Check Response Headers Directly
curl -I -H "Accept-Encoding: gzip" https://example.com/api/data
Look for:
Cache-Control— what directives are setAge— how long the response has been in cache (CDN-added)CF-Cache-Status— Cloudflare's cache status (HIT,MISS,EXPIRED,BYPASS)X-Cache— similar header from other CDN providers
The Age Header
The Age header tells you how old a cached response is in seconds. If you're getting Age: 3540 on a response with max-age=3600, you know you're getting a cached response that's nearly expired.
Force Cache Bypass
Most CDNs support a cache bypass via request headers for debugging:
# Cloudflare cache bypass
curl -I -H "Cache-Control: no-cache" https://example.com/resource
Use Browser DevTools
The Network tab in browser DevTools shows cache status. Look for (from disk cache), (from memory cache), or 304 Not Modified responses. The Timing tab shows whether a request was served from cache (near-zero time) or hit the network.
Performance Impact: What Good Caching Actually Achieves
Proper caching configuration can have dramatic effects on both user experience and infrastructure costs. Some concrete numbers:
- A cached response from a CDN edge node typically has latency of 1-10ms versus 50-500ms for an origin request
- Bandwidth costs drop proportionally to your cache hit ratio — a 90% cache hit ratio means 90% less origin bandwidth
- Compute costs (especially relevant for serverless/Workers) drop with cache hits since origin code never executes
The Website Performance Analyzer runs Lighthouse-based audits that include cache policy checks — it flags resources that are missing Cache-Control headers or have TTLs that are too short for static assets.
For SEO, caching also matters indirectly. Google's Core Web Vitals are heavily influenced by response times, and a well-cached site with fast TTFB (Time to First Byte) has a measurable advantage. The SEO Audit tool can help you identify performance issues that might be affecting your search rankings.
Putting It All Together: A Cache Header Strategy
Rather than copying headers from examples, think through your caching strategy systematically:
Classify your responses — Is this public or private? Static or dynamic? How often does it change?
Choose your TTL — Based on change frequency and acceptable staleness. Use
stale-while-revalidateto soften the edges.Use content hashing for static assets — Enables
immutablecaching with instant cache busting via URL changes.Set appropriate
Varyheaders — Especially for content negotiation, but avoidVary: User-Agent.Test with real tools — Don't assume your headers are correct. Verify with
curl, browser DevTools, or dedicated inspection tools.Monitor cache hit rates — Most CDNs expose this in their dashboards. A hit rate below 70% usually indicates a configuration problem.
The arrival of Worker-level caches makes this discipline even more important. When your cache headers directly control whether your serverless code runs at all, the cost of getting them wrong — in both performance and money — is higher than ever.
Conclusion
HTTP caching headers are one of those foundational web technologies that reward deep understanding. They're not just performance hints — they're contracts between your server, intermediate caches, and browsers that affect security, cost, and user experience simultaneously.
With edge computing platforms adding increasingly sophisticated caching layers configured through these same standard headers, the investment in understanding them properly has never paid off more.
If you want to audit your current caching configuration, check for security header issues, or analyze your site's overall performance, OpDeck provides a suite of tools — including the Cache Inspector, Vulnerability Scanner, and Website Performance Analyzer — to help you identify and fix issues quickly, without setting up complex local tooling.