How to fix Gatsby, Ghost, and Netlify Cache Busting and Service Worker Issues
"In this post, I discuss an issue when Gatsby does not refresh and display new content upon Ghost CMS update, and how to solve such an issue."
Ever since I started making this blog, one issue stand out to me in particular. Whenever I publish a new post or edit a published one, the new changes won't be seen by someone who have visited the site previously, unless they refresh their browser twice.
At first, this seems like a caching issues, and technically speaking it is. However, most Google search point me to adding these magic lines to your static/_headers file
to disable caching for these resources.
# This is recommended to prevent the caching of the service worker file itself
/sw.js # Gatsby's default service worker file path
Cache-Control: no-cache
/*.html
Cache-Control: no-cache
/public/page-data/*.json
Cache-Control: no-cache
That, however, did not resolve my issues.
My blog doesn't have much repeating visitors yet so I kind of left it as is.
As I write more and more though, I finally come down to digging deeper into the issue and found a fix!
gastby-plugin-offline
gastby-plugin-offline
(docs) is a nice little plugin that allow our Gatsby site to work offline. It does that by utilizing Service Worker—specifically Workbox—that Gatsby already use by default to cache our site for offline use.
The problem is, the default runtimeCaching
used by gastby-plugin-offline
uses StaleWhileRevalidate
handling method for our page-date.json
files.
runtimeCaching: [
{
// Use cacheFirst since these don't need to be revalidated (same RegExp
// and same reason as above)
urlPattern: /(\.js$|\.css$|static\/)/,
handler: `CacheFirst`,
},
{ // page-data.json files, static query results and app-data.json // are not content hashed urlPattern: /^https?:.*\/page-data\/.*\.json/, handler: `StaleWhileRevalidate`, }, {
// Add runtime caching of various other page resources
urlPattern: /^https?:.*\.(png|jpg|jpeg|webp|svg|gif|tiff|js|woff|woff2|json|css)$/,
handler: `StaleWhileRevalidate`,
},
{
// Google Fonts CSS (doesn't end in .css so we need to specify it)
urlPattern: /^https?:\/\/fonts\.googleapis\.com\/css/,
handler: `StaleWhileRevalidate`,
},
],
We need to change this to NetworkFirst
to tell SW to always prioritize fetching new data unless we are offline.
To override the default configuration, you can pass a workboxConfig
object as an options
to the plugin. Update the entry in your gatsby-config.js
like this:
// gatsby-config.js
// Your gatsby config's plugins array
plugins: [
// ... ,
{
resolve: `gatsby-plugin-offline`,
options: {
workboxConfig: {
runtimeCaching: [
{
// Use cacheFirst since these don't need to be revalidated (same RegExp
// and same reason as above)
urlPattern: /(\.js$|\.css$|static\/)/,
handler: `CacheFirst`,
},
{
// page-data.json files, static query results and app-data.json
// are not content hashed
urlPattern: /^https?:.*\/page-data\/.*\.json/,
handler: `NetworkFirst`, },
{
// Add runtime caching of various other page resources
urlPattern: /^https?:.*\.(png|jpg|jpeg|webp|svg|gif|tiff|js|woff|woff2|json|css)$/,
handler: `StaleWhileRevalidate`,
},
{
// Google Fonts CSS (doesn't end in .css so we need to specify it)
urlPattern: /^https?:\/\/fonts\.googleapis\.com\/css/,
handler: `StaleWhileRevalidate`,
},
],
},
},
},
]
Now, this will cause more bandwidth to be consumed for the users since they will always have to fetch new page-data.json
for every pages, even if changes are usually rare and far between. Nevertheless, these page-data.json
files are almost always very small, and other assets like images will still be cached so that shouldn't be much of a problem. NetworkFirst
will also still allow our site to function when offline.
Note
The default behaviour, StaleWhileRevalidate
is supposed to tell the SW to display site using what is stored in cache while it silently fetch any new changes and update its cache. Then, upon next visit, present the newly updated version while repeating the silent fetching again.
Apparently, Gatsby will also refresh itself upon any navigation in the site, if changes are present. For some reason though, these behaviour do not work for me. Thus, why I resorted to simply switching to NetworkFirst
instead.
For more information about how each handling method works for service workers, Ondrej Polesny has done an amazing work explaining these in his blog post right here. You should go check that out!