Caching JSON with Cloudflare

Viewed 16362

I am developing a backend system for my application on Google App Engine.

My application and backend server communicating with json. Like http://server.example.com/api/check_status/3838373.json or only http://server.example.com/api/check_status/3838373/

And I am planning to use CloudFlare for caching JSON pages.

Which one I should use on header? :

Content-type: application/json
Content-type: text/html

Is CloudFlare cache my server's responses to reduce my costs? Because I'll not use CSS, image, etc.

4 Answers

By default, Cloudflare does not cache JSON file. I've ended up with config a new page rule:

https://example.com/sub-directiory/*.json*

  • Cache level: Cache Everything
  • Browser Cache TTL: set a timeout
  • Edge Cache TTL: set a timeout

Cloudflare page rule for json

Hope it saves someone's day.

The new workers feature ($5 extra) can facilitate this:

Important point: Cloudflare normally treats normal static files as pretty much never expiring (or maybe it was a month - I forget exactly).

So at first you might think "I just want to add .json to the list of static extensions". This is likely NOT want you want with JSON - unless it really rarely changed - or is versioned by filename. You probably want something like 60 seconds or 5 minutes so that if you update a file it'll update within that time but your server won't get bombarded with individual requests from every end user.

Here's how I did this with a worker to intercept all .json extension files:

// Note: there could be tiny cut and paste bugs in here - please fix if you find!
addEventListener('fetch', event => {
  event.respondWith(handleRequest(event));
});

async function handleRequest(event)
{
  let request = event.request;
  let ttl = undefined;
  let cache = caches.default;      
  let url = new URL(event.request.url);

  let shouldCache = false;


  // cache JSON files with custom max age
  if (url.pathname.endsWith('.json'))
  {
    shouldCache = true;
    ttl = 60;
  }

  // look in cache for existing item
  let response = await cache.match(request);

  if (!response) 
  {       
    // fetch URL
    response = await fetch(request);

    // if the resource should be cached then put it in cache using the cache key
    if (shouldCache)
    {
      // clone response to be able to edit headers
      response = new Response(response.body, response);

      if (ttl) 
      {
        // https://developers.cloudflare.com/workers/recipes/vcl-conversion/controlling-the-cache/
        response.headers.append('Cache-Control', 'max-age=' + ttl);
      }

      // put into cache (need to clone again)
      event.waitUntil(cache.put(request, response.clone()));
    }

    return response;
  }

  else {
    return response;
  }
}

You could do this with mime-type instead of extension - but it'd be very dangerous because you'd probably end up over-caching API responses.

Also if you're versioning by filename - eg. products-1.json / products-2.json then you don't need to set the header for max-age expiration.

You can cache your JSON responses on Cloudflare similar to how you'd cache any other page - by setting the Cache-Control headers. So if you want to cache your JSON for 60 seconds on the edge (s-maxage) and the browser (max-age), just set the following header in your response:

Cache-Control: max-age=60, s-maxage=60

You can read more about different cache control header options here:

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control

Please note that different Cloudflare plans have different value for minimum edge cache TTL they allow (Enterprise plan allows as low as 1 second). If your headers have a value lower than that, then I guess they might be ignored. You can see the limits here:

https://support.cloudflare.com/hc/en-us/articles/218411427-What-does-edge-cache-expire-TTL-mean-#summary-of-page-rules-settings

Related