Caching, Azure and Static Sites (Create React App)
Tags: caching, azure, javascript, cra
Published: 2022-11-23 23:28:30

The [Create React App](https://create-react-app.dev/) is a template starter that greatly assists with bootstrapping applications. Running the build command for this framework will output static files you can drop into Azure and host. This is awesome and easy but there's a hidden gotcha when it comes to frequent deployments of these files. By default, there are no cache-control headers set for these blob containers, even if hosted via frontdoor. This means two things. 

1. Frontdoor (if you're using it) will cache these files for 1-3 days if no cache-control header is present.
2. If no cache control header is set, browsers will attempt [heuristic freshness](https://mdn1.moz.one/en-US/docs/Web/HTTP/Caching#freshness_lifetime) 

What's heuristic freshness????

> A response's freshness lifetime is the length of time between its generation by the origin server and its expiration time. The freshness lifetime for a response is calculated based on several headers:
> 1. If the cache is shared and a Cache-Control header with a s-maxage=N directive is present, the freshness lifetime is N.
> 2. If a Cache-Control header with a max-age=N directive is present, the freshness lifetime is N.
> 3. If an Expires header is present, the freshness lifetime is its value minus the Date header value.
> 4. Otherwise, no explicit expiration time is present in the response. A heuristic freshness checking approach might be applicable
> If an origin server does not explicitly specify freshness (for example, using a Cache-Control or Expires header), then a heuristic approach may be used.
> If this is the case, look for a Last-Modified header. If the header is present, then the cache's freshness lifetime is equal to the value of the Date header minus the value of the Last-modified header divided by 10. The expiration time is computed as follows:
> expirationTime = responseTime + freshnessLifetime - currentAge

TL;DR you should control cache-control.

OK - how do we do that? You can do this at many levels but I prefer doing it at deployment time. Most proxies and the like generally respect cache headers of the origin if they're set, so you get slightly more direct and reliable control (and caching is hard enough as it is without involving multiple layers).

If you use pipelines or run a script to upload the files, I'd suggest using [az](https://learn.microsoft.com/en-us/cli/azure/?view=azure-cli-latest). It's a command line tool for communicating with Azure.

Now when we get to the point of uploading the files to Azure, we effectively want to split it into two steps. One to upload the files we want to cache ([everything under static according to the CRA docs](https://create-react-app.dev/docs/production-build/#static-file-caching)). Then one to upload everything else which we don't want to cache.

We want to upload these files in two steps. One step to upload everything with no-cache and another step to upload the static directory and set the cache max-age header.

First pass:
```
az storage blob upload-batch `
    --account-name $storageAccountName `
    --destination '$web' `
    --source $sourceFolderPath `
    --content-cache 'no-cache'
```
Second pass:
```
az storage blob upload-batch `
    --account-name $storageAccountName `
    --destination '$web' `
    --source $sourceFolderPath `
    --pattern static/* `
    --overwrite true `
    --content-cache 'max-age=31536000'
```

![example screenshot of Azure with the cache control headers set](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/62s0t816uxkxchzzt1ue.png)

Now if you navigate to your blob container you should see the correct cache-control headers set for the files you want cached and not cached. You should also see the headers returned in browser devtools.

Happy cache-or-not'ing