Enable CDN (Cloudflare) caching  for your Netlify site
Tags: netlify, jamstack, cdn, webdev
Published: 2019-04-14 23:15:45

---
title: Enable CDN (Cloudflare) caching  for your Netlify site
published: true
tags: Netlify, jamstack, CDN, webdev
canonical_url: https://codewithhugo.com/enable-cdn-netlify/
---

This post goes through how to reduce the amount of static assets served through Netlify by leveraging a CDN (Content Delivery Network) like Cloudflare. This will reduce your Netlify bandwidth usage for cacheable assets.

It will also mean that you’re following industry-accepted best practices around setting a time for which your assets should be cached by receiving browsers.

In principle, it won’t significantly improve the speed at which your site loads since Netlify’s built-in CDN does a good job of that anyways. It will mean that you’re leveraging your CDN’s capacity to deliver assets quickly as opposed to Netlify’s.

## What is Netlify’s default caching behaviour?

The default Netlify cache-control header, are `"max-age=0, must-revalidate, public"` which means all content should be cached but also re-validated. The rationale is that “This favors you as a content creator — you can change any of your content in an instant.” - [Better Living Through Caching](https://www.netlify.com/blog/2017/02/23/better-living-through-caching/).

This means that web performance tests will give you lower marks for not having a reasonable caching length for static assets.

Netlify suggests not to use a CDN in front of it (see [Why You Don’t Need Cloudflare with Netlify](https://www.netlify.com/blog/2017/03/28/why-you-dont-need-cloudflare-with-netlify/)). The core points of this article is that Netlify provides the following which a CDN also tend to help with:

- SSL: Netlify creates certificates so that your site is always served over HTTPS.
- CDN: Netlify’s CDN is very clever when used along its managed DNS service, with smart load balancing/routing and DDoS prevention baked in.
- DNS: Setting up (custom) domains with Netlify’s DNS is fast and simple.

These 3 services are services also provided by Cloudflare.

## Setting caching headers on Netlify

The Netlify documentation for [Custom headers](https://www.netlify.com/docs/headers-and-basic-auth/#custom-headers) doesn’t have a caching example. It also suggests there are 2 ways to add custom headers: through a `_headers` file and as part of a `[[headers]]` section of your `netlify.toml`.

Here’s a sample `netlify.toml` setup for [codewithhugo.com](https://codewithhugo.com), ignore the `[build]` section or see the [The netlify.toml File](https://www.netlify.com/docs/netlify-toml-reference/) article in Netlify’s knowledge base:

```
[build]
  command = "./scripts/build.sh"
  publish = "public"
  functions = "lambda"

[[headers]]
  for = "/img/*"
  [headers.values]
    Cache-Control = "public, s-max-age=604800"
[[headers]]
  for = "/*.css"
  [headers.values]
    Cache-Control = "public, s-max-age=604800"
[[headers]]
  for = "/*.js"
  [headers.values]
    Cache-Control = "public, s-max-age=604800"
```

The equivalent `_headers` file:

```
/img/*
  Cache-Control: public, s-max-age=604800
/*.css
  Cache-Control: public, s-max-age=604800
/*.js
  Cache-Control: public, s-max-age=604800

```

The `Cache-Control` we’re setting is `public, s-max-age=604800`. We’re using `s-max-age` which according to the [MDN Cache-Control article](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#Expiration) is:

> Takes precedence over `max-age` or the `Expires` header, but it only applies to shared caches (e.g., proxies) and is ignored by a private cache.

This means we’re using custom headers on Netlify to tell **Cloudflare (our CDN)** how long to cache assets. We’ll then control how long **browsers** cache using CDN settings.

604800 seconds translates to 7 days, so during normal operation Cloudflare will cache images, CSS and JavaScript files for 7 days.

## Cache rules breakdown

From Cloudflare statistics I know that images + CSS account for more than 60% of my traffic.

### Image caching

Serving images out of Netlify doesn’t seem too useful since they don’t change often. They’re also not actually part of the core [codewithhugo.com](https://codewithhugo.com) offering, they’re “nice-to-have” as opposed to the “must-have” text + code content. That’s where the first rule comes in:

```
[[headers]]
  for = "/img/*"
  [headers.values]
    Cache-Control = "public, s-max-age=604800"
```

or in `_headers` format:

```
/img/*
  Cache-Control = "public, s-max-age=604800"

```

### CSS caching

The CSS I generate through Hugo has a cache-busting hash baked in. So old versions and new versions will be fetched and cached separately. It’s therefore safe to aggressively cache across the CDN and in browsers.

In `netlify.toml` headers format:

```
[[headers]]
  for = "/*.css"
  [headers.values]
    Cache-Control = "public, s-max-age=604800"
```

In `_headers` format:

```
/*.css
  Cache-Control: public, s-max-age=604800

```

### JavaScript caching

I don’t leverage a lot of JavaScript and whatever I do use usually ends up as a couple of script tags on a page. The pre-built JavaScript that I consume is safe to cache for long periods.

In `netlify.toml` headers format:

```
[[headers]]
  for = "/*.js"
  [headers.values]
    Cache-Control = "public, s-max-age=604800"
```

In `_headers` format:

```
/*.js
  Cache-Control: public, s-max-age=604800

```

## Caching Results

My cached requests percentage has gone from 5% to over 65%. Since Cloudflare is serving most of my heaviest content (images), it’s resulted in an 80% Cloudflare → Netlify bandwidth usage reduction.

> Caching assets with [@Cloudflare](https://twitter.com/Cloudflare?ref_src=twsrc%5Etfw)
> 
> Left ~5%, default Netlify headers ()
> 
> Right 50%+, with Cache-Control: public, s-max-age=604800
> 
> ie. Allow a CDN in front of this site to cache assets 1 week
> 
> ![pic.twitter.com/QijWByIbOF](https://pbs.twimg.com/media/D2F3tpsX0AADboq.jpg)
> 
> — Hugo Di Francesco (@hugo\_\_df) [March 20, 2019](https://twitter.com/hugo__df/status/1108298641100410882?ref_src=twsrc%5Etfw)

[<svg xmlns="http://www.w3.org/2000/svg" style="height:12px;width:auto;position:relative;vertical-align:middle;top:-2px;fill:white" viewbox="0 0 32 32"><title>unsplash-logo</title>
<path d="M10 9V0h12v9H10zm12 5h10v18H0V14h10v9h12v-9z"></path></svg>Zbynek Burival](https://unsplash.com/@zburival?utm_medium=referral&utm_campaign=photographer-credit&utm_content=creditBadge "Download free do whatever you want high-resolution photos from Zbynek Burival")