© 2026 Hedgehog Software, LLC

TwitterGitHubDiscord
More
CommunitiesDocsAboutTermsPrivacy
Search
Star
Setup for Free
Cloudflare DevelopersCD
Cloudflare Developers•3y ago•
1 reply
Apoc

Workers fetch cache (tiered caching) - How to handle conditional caching?

I have a use-case where we'd like to utilize tiered caching instead of the cache API to help shield our origins from requests, but I'm struggling to replicate the same logic using the fetch cache. Our worker essentially acts as a reverse proxy, handling internal routing & query parameters to the origins (multiple servers).

This is a very slimmed down version of our logic using the cache API:

export async function getReverseProxyResponseCacheApi(request: Request, requestRules: RequestRules, pageProviderUrl: string): Promise<Response> {
    // Disable caching when requesting on an internal IP
    const useCache = !SecurityService.isBusinessIpRequest(request);

    const cache = caches.default;

    // Appends various information to the URL to ensure that the cache key is unique. We include internal data
    // such as experiments, device, region, currency info, etc.
    const cacheKey = CacheKeyService.getCacheKey(request, pageProviderUrl);

    let page = useCache ? await cache.match(cacheKey) : undefined;

    if (!page) {
        const pageProviderResponse = await fetch(pageProviderUrl);
        page = new Response(pageProviderResponse.body, pageProviderResponse);

        if (useCache && page.ok) {
            const cacheControl = pageProviderResponse.headers.get('cache-control');
            // If the cache is not public, or set to no-store, don't cache
            if (cacheControl && !cacheControl.includes('no-store') && cacheControl.includes('public')) {
                page.headers['Cache-Tag'] = CacheKeyService.getCacheTags(request, requestRules);

                // Respects the cache-control header TTL that was present on the origin response
                await cache.put(cacheKey, page);
            }
        }
    }

    // If the page still isn't found, throw an error
    if (!page) {
        throw new Error('Page not found');
    }

    return page;
}
export async function getReverseProxyResponseCacheApi(request: Request, requestRules: RequestRules, pageProviderUrl: string): Promise<Response> {
    // Disable caching when requesting on an internal IP
    const useCache = !SecurityService.isBusinessIpRequest(request);

    const cache = caches.default;

    // Appends various information to the URL to ensure that the cache key is unique. We include internal data
    // such as experiments, device, region, currency info, etc.
    const cacheKey = CacheKeyService.getCacheKey(request, pageProviderUrl);

    let page = useCache ? await cache.match(cacheKey) : undefined;

    if (!page) {
        const pageProviderResponse = await fetch(pageProviderUrl);
        page = new Response(pageProviderResponse.body, pageProviderResponse);

        if (useCache && page.ok) {
            const cacheControl = pageProviderResponse.headers.get('cache-control');
            // If the cache is not public, or set to no-store, don't cache
            if (cacheControl && !cacheControl.includes('no-store') && cacheControl.includes('public')) {
                page.headers['Cache-Tag'] = CacheKeyService.getCacheTags(request, requestRules);

                // Respects the cache-control header TTL that was present on the origin response
                await cache.put(cacheKey, page);
            }
        }
    }

    // If the page still isn't found, throw an error
    if (!page) {
        throw new Error('Page not found');
    }

    return page;
}


However, with the fetch cache, we don't have the same control as far as I can tell.

export async function getReverseProxyResponseFetchCacheApi(request: Request, requestRules: RequestRules, pageProviderUrl: string): Promise<Response> {
    const useCache = SecurityService.isBusinessIpRequest(request);
    const cacheKey = CacheKeyService.getCacheKey(request, pageProviderUrl);

    let page = await fetch(pageProviderUrl, {
        cf: {
            cacheTtl: useCache ? requestRules.reverseProxyTtl : undefined,
            cacheTtlByStatus: {
                '300-599': 0,
            },
            cacheKey: cacheKey,
            cacheTags: [CacheKeyService.getCacheTags(request, requestRules)],
        },
    });

    page = new Response(page.body, page);

    // Cache-Control is completely ignored by the tiered cache since we provided a TTL
    const cacheControl = page.headers.get('cache-control');

    // If the cache is not public, or set to no-store, don't cache
    if (!page.ok || (cacheControl && (!cacheControl.includes('public') || cacheControl.includes('no-store')))) {
        // How do we handle this?
        // await caches.default.delete(cacheKey);
        return page;
    }

    return page;
}
export async function getReverseProxyResponseFetchCacheApi(request: Request, requestRules: RequestRules, pageProviderUrl: string): Promise<Response> {
    const useCache = SecurityService.isBusinessIpRequest(request);
    const cacheKey = CacheKeyService.getCacheKey(request, pageProviderUrl);

    let page = await fetch(pageProviderUrl, {
        cf: {
            cacheTtl: useCache ? requestRules.reverseProxyTtl : undefined,
            cacheTtlByStatus: {
                '300-599': 0,
            },
            cacheKey: cacheKey,
            cacheTags: [CacheKeyService.getCacheTags(request, requestRules)],
        },
    });

    page = new Response(page.body, page);

    // Cache-Control is completely ignored by the tiered cache since we provided a TTL
    const cacheControl = page.headers.get('cache-control');

    // If the cache is not public, or set to no-store, don't cache
    if (!page.ok || (cacheControl && (!cacheControl.includes('public') || cacheControl.includes('no-store')))) {
        // How do we handle this?
        // await caches.default.delete(cacheKey);
        return page;
    }

    return page;
}


Is there any way to work around this that doesn't involve using the cache purge API from within the worker? (250k purges per day would quickly be hit)
Cloudflare Developers banner
Cloudflare DevelopersJoin
Welcome to the official Cloudflare Developers server. Here you can ask for help and stay updated with the latest news
85,042Members
Resources
Was this page helpful?

Similar Threads

Recent Announcements

Similar Threads

Caching worker responses in Tiered Caching with Fetch API, similar to Cache API.
Cloudflare DevelopersCDCloudflare Developers / workers-and-pages-help
11mo ago
How to cache fetch result in workers ?
Cloudflare DevelopersCDCloudflare Developers / workers-and-pages-help
3y ago
Workers fetch cache not working?
Cloudflare DevelopersCDCloudflare Developers / workers-and-pages-help
3y ago
Workers caching and sec-fetch-mode: navigate
Cloudflare DevelopersCDCloudflare Developers / workers-and-pages-help
5mo ago