12

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.

Sirko
  • 72,589
  • 19
  • 149
  • 183
ecabuk
  • 1,641
  • 1
  • 18
  • 20

4 Answers4

26

The standard Cloudflare cache level (under your domain's Performance Settings) is set to Standard/Aggressive, meaning it caches only certain types by default scripts, stylesheets, images. Aggressive caching won't cache normal web pages (ie at a directory location or *.html) and won't cache JSON. All of this is based on the URL pattern (e.g. does it end in .jpg?) and regardless of the Content-Type header.

The global setting can only be made less aggressive, not more, so you'll need to setup one or more Page Rules to match those URLs, using Cache Everything as the custom cache rule.

http://blog.cloudflare.com/introducing-pagerules-advanced-caching

BTW I wouldn't recommend using an HTML Content-Type for a JSON response.

mahemoff
  • 44,526
  • 36
  • 160
  • 222
echtish
  • 548
  • 1
  • 5
  • 9
11

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.

manroe
  • 1,645
  • 20
  • 35
mihnsen
  • 385
  • 3
  • 8
5

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.

Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
  • Also if you're still seeing caching not working it could be something like `Authorization` header which prevents caching from occuring. This may or may not matter but for non-secret static .json files it probably doesn't - and you can remove the header above. – Simon_Weaver May 10 '19 at 00:26
  • 1
    I'm gonna add a comment here because this is excellent example code. – Toby Jul 17 '19 at 18:50
1

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

amit_saxena
  • 7,450
  • 5
  • 49
  • 64
  • Unfortunately, I tried a few things (ultimately I had all of `public, s-maxage=60, max-age=60` set in my server-provided headers) and CF still refuses to cache JSON. Page Rules are required here, specifically with the "Cache Level=Cache everything" setting. – manroe Aug 09 '22 at 20:04