-
-
Notifications
You must be signed in to change notification settings - Fork 27.1k
Description
Problem
When CRA app is served over a CDN, the Cache headers suggested by the docs are as follows:
index.html Cache-Control: no-cache
js/css/img Cache-Control: max-age: 31536000
Doing this way, the problem is that whenever browser fetches index.html, it would need to validate with the origin server (lets say s3) if its local copy is valid, and if its not expired (using ETag) browser can use local cache copy, or else it would download the html. Though this doesn't incur bandwidth (when cached copy is still valid), it would incur a roundtrip latency each time to load index.html. In some sense, defeats the purpose of serving the app over CDN, ideally the index.html should be served directly from the CDN without setting Cache-Control: no-cache on it.
Why Cache-Control: no-cache is needed?
Because the generated index.html is modified each time the static bundle files change. If the index.html is not configured with Cache-Control: no-cache, the browser may get a older/cached index.html from CDN, referencing older static bundles. When it would try to download those older static files from the CDN, and in case there is a cache miss, it would result in 404s as the origin server no longer has those older static files.
Proposed Solution
Generate the static output files in a versioned folder instead of build/static:
my-app
|__ build
|__ static
|___<versioned-folder-hash>
|__ js
|__css
|__media
index.html:
<script src="/static/<versioned-folder-hash>/js/2.chunk.js"></script>
<script src="/static/<versioned-folder-hash>/js/main.chunk.js"></script>
asset-manifest.json:
{
"files" : {
"main.js": "/static/<versioned-folder-hash>/js/main.chunk.js",
...
},
"entrypoints": [
"static/<versioned-folder-hash>/js/main.chunk.js"
],
"version": "<versioned-folder-hash>"
}
Advantages of above build output structure:
-
Better caching of index.html:
Cache-Control: no-cacheis no longer needed on index.html.
a) Whenever a new build is deployed to your origin server, you can run a script to cache bust index.html. It would take a few mins for all edge servers to reflect the index.html, in that time window, even if some browsers get served with older index.html from CDN, the older static bundles referenced in that index.html would still be present in the Origin server (you don't delete then-1release bundle yet. The clean up of older build can be done aftern+1release, by then all edge servers would be serving releasen).b) Until the next release is deployed, the index.html file would be served directly from CDNs, and no longer have the roundtrip latency of validating the freshness of the cached file from Origin server.
App deploy folder structure on Origin Server would potentially look like this:
<app-root>
|___ index.html
|___ asset-manifest.json
|___ static
|___<versioned-folder-hash-1> // release 'n'
|__ js
|__css
|__media
|___<versioned-folder-hash-2> // release 'n-1'
Current Alternative to achieve the above:
Though I don't want to go this route, I am probably going to have to eject the config from cra and make modifications in config files to achieve the above build output structure.
I would be glad if someone in the community can help me understand if the above problem is solvable in some other way, without having to eject the configs / my understanding of the Caching problem is inaccurate.