Default Browser Cache TTL Gotcha with Cloudflare

Asset Loading

In my last article I shared how I upgraded a Rails app from Sprockets to Propshaft. This was a great simplification and improvement overall. But something wasn’t quite right. I kept noticing that there was a latency on the home page as some JavaScript was being loaded.

Locally things were lightning fast, and it was starting to bug me what the issue could be. Opening up the web inspector I noticed that locally the cache-control header was being set with max-age=31536000 which is the Rails default and described in the Rails Guides here and equates to 1 year in seconds.

But in production max-age was being set to 14400 which is 4 hours! Here’s what it looked like comparing the responses in the Web Inspector:

Comparison of max-age locally vs production

Cloudflare

What I didn’t mention in the last article, is that I use Cloudflare for DNS and managing SSL certificates and it works great out of the box. They also provide a global cache built in which is great! Or is it…

Cloudflare puts it’s cache in front of the webserver which is what we want, but I hadn’t realized that the default cache time is 4 hours! That rings a bell?!

Cloudflare Browser Cache TTL

Cache Rules

Since our lovely new asset files are fingerprinted by Propshaft whenever they change, we can afford to just instruct client browsers to cache them for as long as possible, since any new version will have a new file name.

In Cloudflare, rather than just changing the global default from 4 hours, we can add our own custom rules. So for our Rails assets we can just say for any file ending with .js or .css set the cache-control header to the maximum, which it turns out is 1 year.

From the Cloudflare main menu, select “Caching” and then “Cache Rules” and you will see the form below where we simply select some rules to match - in this case that the URI Path ends with ‘js’ or ‘css’ - and then the caching behaviour we want to apply. In my case I just decided to set to the maximum 1 year.

Cloudflare Cache Rule

Result

The result was now my production service was super snappy again. We can also see from the web inspector that the cache-control is correctly set, and we can see from the timings that after our change, the waiting time is 0ms!

Before and After

Wrap Up

Would love to hear your experiences with caching, or other ideas on optimisation. Don’t hesitate to drop me a note via twitter or any other channels listed here if you have questions, improvements or corrections!