Looking for input on my approach to improve Next.js and Netlify concerning differing caching strategies

This is not exactly a support topic, so I put it here. Feel free to move it.

I have encountered problems with differing cache busting strategies between Next.js and Netlify.

Netlify uses a unique approach (Better Living Through Caching) that seems to benefit the most from persisting file names when file contents have not truly changed. Next.js on the other hand, wants to use file name based cache busting to make sure stale files are not used.

I discovered the following:

  1. Republishing a globally referenced page from a CMS (header or footer reference for example) caused destination page to have Next.js rehash file name. We use Contentful as a CMS.
  2. Next.js default use of link prefetching caused header or footer references to be updated.
  3. Updated references caused all pages to be “different” due to the changed hash based file names.
  4. Netlify would need to reprocess and upload all 16,000 page far too frequently. And this added 40-50 minutes total deploy time.

I asked in the Next.js discussion about possibility to disable use of hashes in file names. Netlify just doesn’t need this. See very short Vercel Next.js discussion, the response:

No. It’s not possible, we do that in order to serve static and cache static files in the browser and to invalidate them when needed.

I found it was easy to remove the Next.js buildId from the path of Next.js builds, but this did not solve all hashed/unique path/filename problems. It only solved a few.

Then I discovered this Gatsby plugin: gatsby-plugin-remove-fingerprints

I configure Next.js in a similar way to the gatsby-plugin-remove-fingerprints, with some added exclusions:

  • Don’t mess with webpack js names when running development server
  • Don’t mess with webpack js names in webpack configuration for server-side, which would include the fantastic next-on-netlify package
const { PHASE_DEVELOPMENT_SERVER } = require('next/constants');

module.exports = (phase) => {
  return {
    target: 'serverless',
    generateBuildId: async () => {
      return 'build';
    webpack: (origConfig, { isServer }) => {
      const { ...config } = origConfig;
      // isServer is used to only apply changes to client use. Otherwise breaks server/serverless use like in next-on-netlify
      if (phase !== PHASE_DEVELOPMENT_SERVER && !isServer) {
        config.output.filename = `static/chunks/[name].js`;
        config.output.chunkFilename = `static/chunks/chunk-[name].js`;
      } else {
        console.log(`Next is operating in phase:${phase}`);
        console.debug(JSON.stringify(config.output, null, 2));
      return config;

We are using this in preliminary production releases, and have seen no problems. We have seen a radical drop in total build times (seconds versus minutes) as often only 1-2 files now need to be processed and uploaded.

I would love to get feedback on what I am doing here from those with more expertise than I.
Like @cassidoo

And messing with Webpack makes me nervous, like holding a cup of water over my laptop nervous.


@moop-moop hey! sorry for the delay getting back to you. thoughts: like the gatsby plugin, we think this should work, but it seems you’ll be the first to try this out in production with nextjs, so i’d definitely pay close attention to it. if this is successful for you, i’d love to see you open source your own plugin!!!