The Cache API provides programmatic control over HTTP caching. You can store responses and retrieve them later, enabling faster subsequent requests and offline functionality.
Overview
Workerd implements a subset of the standard Cache API:
caches.default - Access the default cache
caches.open(name) - Open a named cache
cache.match() - Retrieve cached responses
cache.put() - Store responses
cache.delete() - Remove cached responses
Implementation: src/workerd/api/cache.h and cache.c++
Accessing caches
Default cache
The caches.default cache is always available:
const cache = caches . default ;
Named caches
Open or create a named cache:
const cache = await caches . open ( 'my-cache' );
Storing responses
Use cache.put() to store responses:
export default {
async fetch ( request ) {
const cache = caches . default ;
// Create a response
const response = new Response ( 'Hello World' , {
headers: {
'Content-Type' : 'text/plain' ,
'Cache-Control' : 'public, max-age=3600'
}
});
// Store in cache
await cache . put ( request , response . clone ());
return response ;
}
} ;
Source: src/workerd/api/tests/cache-operations-test.js:52
The Cache-Control header determines caching behavior:
// Cache for 1 hour
const response = new Response ( 'data' , {
headers: {
'Cache-Control' : 'public, max-age=3600'
}
});
await cache . put ( request , response );
// Don't cache
const response = new Response ( 'data' , {
headers: {
'Cache-Control' : 'no-store'
}
});
await cache . put ( request , response ); // Won't be cached
// Server-specific max age
const response = new Response ( 'data' , {
headers: {
'Cache-Control' : 's-maxage=7200, public'
}
});
await cache . put ( request , response );
Source: src/workerd/api/tests/cache-operations-test.js:65
Retrieving responses
Use cache.match() to retrieve cached responses:
export default {
async fetch ( request ) {
const cache = caches . default ;
// Try to get from cache
let response = await cache . match ( request );
if ( response ) {
// Cache hit
return response ;
}
// Cache miss - fetch from origin
response = await fetch ( request );
// Store in cache for next time
await cache . put ( request , response . clone ());
return response ;
}
} ;
Match options
Customize matching behavior:
// Ignore HTTP method (match non-GET requests)
const response = await cache . match ( request , {
ignoreMethod: true
});
Source: src/workerd/api/cache.h:16
Cloudflare’s implementation does not support ignoreSearch or ignoreVary options. Users can remove query parameters before calling put() if needed.
Deleting cached responses
Remove responses from the cache:
const cache = caches . default ;
// Delete a specific response
const deleted = await cache . delete ( request );
if ( deleted ) {
console . log ( 'Cache entry deleted' );
} else {
console . log ( 'Cache entry not found' );
}
Source: src/workerd/api/tests/cache-operations-test.js:97
Delete options
Use the same options as match():
// Delete non-GET requests
const deleted = await cache . delete ( request , {
ignoreMethod: true
});
Cache patterns
Cache-first strategy
Serve from cache, fall back to network:
export default {
async fetch ( request ) {
const cache = caches . default ;
// Try cache first
let response = await cache . match ( request );
if ( ! response ) {
// Fetch from network
response = await fetch ( request );
// Cache the response
await cache . put ( request , response . clone ());
}
return response ;
}
} ;
Network-first strategy
Try network first, fall back to cache:
export default {
async fetch ( request ) {
const cache = caches . default ;
try {
// Try network first
const response = await fetch ( request );
// Update cache
await cache . put ( request , response . clone ());
return response ;
} catch ( error ) {
// Network failed, try cache
const cached = await cache . match ( request );
if ( cached ) {
return cached ;
}
// No cache entry either
return new Response ( 'Offline' , { status: 503 });
}
}
} ;
Stale-while-revalidate
Serve stale content while updating:
export default {
async fetch ( request , env , ctx ) {
const cache = caches . default ;
// Get cached response
const cached = await cache . match ( request );
// Fetch fresh response asynchronously
const fetchPromise = fetch ( request ). then ( response => {
// Update cache in background
ctx . waitUntil (
cache . put ( request , response . clone ())
);
return response ;
});
// Return cached response immediately if available
return cached || fetchPromise ;
}
} ;
Time-based invalidation
Invalidate cache entries after a time period:
export default {
async fetch ( request ) {
const cache = caches . default ;
const response = await cache . match ( request );
if ( response ) {
// Check age
const date = new Date ( response . headers . get ( 'Date' ));
const age = Date . now () - date . getTime ();
// If older than 1 hour, refresh
if ( age > 3600000 ) {
const fresh = await fetch ( request );
await cache . put ( request , fresh . clone ());
return fresh ;
}
return response ;
}
// Not in cache
const fresh = await fetch ( request );
await cache . put ( request , fresh . clone ());
return fresh ;
}
} ;
Cache keys
Cache keys are based on the request URL:
// These are different cache entries
await cache . put (
new Request ( 'https://example.com/api' ),
response1
);
await cache . put (
new Request ( 'https://example.com/api?param=value' ),
response2
);
Custom cache keys
Create custom cache keys by modifying the request:
// Remove query parameters for cache key
function getCacheKey ( request ) {
const url = new URL ( request . url );
url . search = '' ; // Remove query string
return new Request ( url . toString (), request );
}
// Use custom key
const cacheKey = getCacheKey ( request );
let response = await cache . match ( cacheKey );
if ( ! response ) {
response = await fetch ( request );
await cache . put ( cacheKey , response . clone ());
}
Best practices
Response bodies can only be read once. Clone before caching: const response = await fetch ( request );
await cache . put ( request , response . clone ());
return response ;
Set appropriate Cache-Control headers
Handle cache misses gracefully
Always check if match() returns undefined: const cached = await cache . match ( request );
if ( ! cached ) {
// Handle cache miss
}
Use named caches for organization
Separate different types of cached content: const apiCache = await caches . open ( 'api-responses' );
const assetCache = await caches . open ( 'static-assets' );
Implementation notes
The Cache API implementation in workerd:
Only supports GET requests by default
Does not support cache enumeration (keys(), matchAll())
Does not support ignoreSearch or ignoreVary options
Cache keys are based on request URL
Definition: src/workerd/api/cache.h:37
class Cache : public jsg :: Object {
jsg :: Promise < jsg :: Optional < jsg :: Ref < Response >>> match (
jsg :: Lock & js ,
Request :: Info request ,
jsg :: Optional < CacheQueryOptions > options ,
CompatibilityFlags :: Reader flags );
jsg :: Promise < void > put (
jsg :: Lock & js ,
Request :: Info request ,
jsg :: Ref < Response > response ,
CompatibilityFlags :: Reader flags );
jsg :: Promise < bool > delete_ (
jsg :: Lock & js ,
Request :: Info request ,
jsg :: Optional < CacheQueryOptions > options ,
CompatibilityFlags :: Reader flags );
};
Fetch API - Making requests and handling responses
Headers - Managing Cache-Control headers