Save Netlify bandwidth by caching assets on Cloudflare
Is there a problem with how Netlify handles caching?
No, it's totally fine. Netlify itself provides a pretty good CDN and caches all cacheable assets by default.
Why use another CDN then?
Netlify suggests that you don't need Cloudflare with it, and they are not wrong, at least for the reasons they talk about, they both provide almost similar features.
Here's a comparison.
Netlify | Cloudflare |
---|---|
Provides free SSL(Let's Encrypt) | Provides free SSL(Cloudflare issued certificates) |
DNS management(backed by NS1) | DNS Management |
Fast CDN, DDoS protection | Fast CDN, DDoS protection |
Limited bandwidth(100 GB in free plan) | Unlimited bandwidth. |
As you can see they both offer a similar feature set, except for the bandwidth limitation.
Since Netlify imposes a limit on the bandwidth, the plan here is to cache and serve content from another CDN which provides a lot more bandwidth(Cloudflare in this case) and reduce the amount of bandwidth consumed directly from the host server.
This will not improve the site performance significantly, we are only trying to save some bandwidth.
Default caching behavior of Netlify
By default, the following cache headers are set for each response max-age=0, must-revalidate, public
.
This means that the content can be cached, but it should also be re-validated every time. Ideally, this process would be very fast with Netlify's fast CDN and some HTTP/2 magic.
This will work fine for users re-visiting your site, but for new users, the content will be served from Netlify's CDN and every time this happens, some of your bandwidth quotas will be consumed.
We want to put Cloudflare in front of Netlify and cache all cacheable resources there, so whenever a new visitor comes in and requests content that was already present in Cloudflare's cache, it will be served directly from there instead of Netlify, saving our bandwidth.
But this is not possible by simply putting Cloudflare in the front, since by default Cloudflare respects the headers received from the origin server. Which in our case instruct Cloudflare to always validate content from the origin server.
To fix this, we need to set some custom headers that Netlify will send with each response.
But first a note on Cache-Control Headers
This is not to scare you, but we need to be careful while setting cache-control headers because messing up with these can lead to your visitors seeing stale or old content.
Resources which change rarely should be cached, this usually includes Images, JavaScript files which don't change often, CSS files etc.
As a rule of thumb never cache HTML files and other files which might change frequently.
You should spend some time understanding this topic in depth. Check out Caching best practices for more details.
You should also go through the documentation of the static site generator or framework you are using once (if you are using any) to find out what their guidelines on this are and if they provide any support for generating these headers for you.
Setting Cache-Control Headers on Netlify
There are multiple ways to set custom headers in a Netlify site.
- Using
_headers
file. - Using
netlify.toml
file.
For more information on this, you can check out the official docs.
Using _headers
file
Here we are telling Netlify to set this Cache-Control Header for all JavaScript, CSS files, and all files in the static folder because that's where my image files are. You might need to change this depending on where your image or other static asset files are.
/static/*
Cache-Control: public, max-age=31536000, immutable
/*.css
Cache-Control: public, max-age=31536000, immutable
/*.js
Cache-Control: public, max-age=31536000, immutable
Using netlify.toml
file
Here's an equivalent toml version.
[[headers]]
for = "/static/*"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"
[[headers]]
for = "/*.css"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"
[[headers]]
for = "/*.js"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"
What this header means
This particular header public, max-age=31536000, immutable
means we are telling the CDN and browser that this file can be cached for a very long time and we are sure that it won't change and there's no need to request it from the server even if the user explicitly refreshes the page.
Now, your use case may differ a bit and you may want to set the headers differently. I suggest you go through the following docs to get a better understanding of this topic.
Bonus if you are using Gatsby
Gatsby has a plugin that automatically generates the required _headers
file for you.
Check out the plugin gatsby-plugin-netlify here on how to use it.
Results
This depends on what type of content you serve through your website.
If your content consists of a lot of images/js then you will probably save a lot of bandwidth.
If your site consists mostly of plain HTML files, then, maybe not so much.
As for me, one of my sites serves a lot of images, and here's the result.