Menu

#3303 Google fonts sometimes fail to load - caching of opaque errors

open
nobody
None
2024-10-30
2024-03-27
Anonymous
No

Originally created by: joshkel

Library Affected: workbox-strategies, workbox-recipes

Browser & Platform: Google Chrome 123.0.6312.58 for Mac

Issue Description:

I've noticed that Google fonts sometimes fail to load for my web app.

We're using a standard <link> tag to request the fonts stylesheet:

    <link
      rel="stylesheet"
      href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
      crossorigin
    />

and our own local version of the Google Fonts recipe, based off the old https://developers.google.com/web/tools/workbox/guides/common-recipes#google_fonts page:

  registerRoute(
    ({ url }) => url.origin === 'https://fonts.googleapis.com',
    new StaleWhileRevalidate({
      cacheName: 'google-fonts-stylesheets',
    })
  );

  registerRoute(
    ({ url }) => url.origin === 'https://fonts.gstatic.com',
    new CacheFirst({
      cacheName: 'google-fonts-webfonts',
      plugins: [
        new CacheableResponsePlugin({
          statuses: [0, 200],
        }),
        new ExpirationPlugin({
          maxAgeSeconds: 60 * 60 * 24 * 365,
          maxEntries: 30,
        }),
      ],
    })
  );

The cause, as best as I can tell, is that Chrome will randomly fail to load the googleapis.com link when the page is first loaded. (It seems to work fine whenever an already open page is reloaded.) I've been unable to find any details as to why. Sample console error messages:

The FetchEvent for "https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" resulted in a network error response: an "opaque" response was used for a request whose type is not non-cors
The FetchEvent for "https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" resulted in a network error response: an "opaque" response was used for a request whose type is not non-cors
The FetchEvent for "https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" resulted in a network error response: an "opaque" response was used for a request whose type is not non-cors
GET https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap net::ERR_FAILED

When this happens, Workbox may cache an opaque response, causing the next page load or reload to not have valid fonts. (I believe that had previously happened in the sample console messages - the previous response had failed, resulting in the FetchEvent messages, then the current response failed too.)

If I modify the recipe to not cache opaque responses, I no longer have these issues:

  registerRoute(
    ({ url }) => url.origin === 'https://fonts.googleapis.com',
    new StaleWhileRevalidate({
      cacheName: 'google-fonts-stylesheets',
      plugins: [
        new CacheableResponsePlugin({
          statuses: [200],
        }),
      ],
    })
  );

  registerRoute(
    ({ url }) => url.origin === 'https://fonts.gstatic.com',
    new CacheFirst({
      cacheName: 'google-fonts-webfonts',
      plugins: [
        new CacheableResponsePlugin({
          statuses: [200],
        }),
        new ExpirationPlugin({
          maxAgeSeconds: 60 * 60 * 24 * 365,
          maxEntries: 30,
        }),
      ],
    })
  );

Based on this, I believe that the recipe's guidance to allow caching of opaque Google fonts responses is out of date.

Discussion

  • Anonymous

    Anonymous - 2024-10-29

    Originally posted by: josuearrieta

    @joshkel same issue here! Did you manage to find a solution?

     
  • Anonymous

    Anonymous - 2024-10-30

    Originally posted by: joshkel

    @josuearrieta Yes, we added CacheableResponsePlugin({ statuses: [200] }) to our version of the recipe to instruct Workbox to only cache valid responses.

    For completion, here's the full code we're using.

    /**
    
     * Configure caching for Google Fonts:
     *
     * - https://web.archive.org/web/20211204153350/https://developers.google.com/web/tools/workbox/guides/common-recipes#google_fonts
     * - https://github.com/GoogleChrome/workbox/blob/v7.0.0/packages/workbox-recipes/src/googleFontsCache.ts#L32
     */
    export function registerGoogleFonts() {
      // NOTE: Workbox recommends allowing caching of opaque responses.  However,
      // we've seen that cause problems: when the page is first loaded, Chrome may
      // fail to retrieve the CSS (with generic "net::ERR_FAILED" errors in the
      // console), which causes unusable opaque responses to be cached, which causes
      // fonts to be missing the _next_ time the page is loaded.  To deal with that,
      // we'll only cache valid (non-opaque) responses.
      //
      // (Apparently, StaleWhileRevalidate caches opaque responses by default, while
      // CacheFirst does not.  That seems like an odd API design; we'll specify
      // CacheableResponsePlugin for both to be explicit and consistent.)
      //
      // See https://github.com/GoogleChrome/workbox/issues/3303 and
      // https://developer.chrome.com/docs/workbox/caching-resources-during-runtime#workbox_may_not_cache_opaque_responses
    
      // Cache the Google Fonts stylesheets with a stale-while-revalidate strategy.
      registerRoute(
        ({ url }) => url.origin === 'https://fonts.googleapis.com',
        new StaleWhileRevalidate({
          cacheName: 'google-fonts-stylesheets',
          plugins: [new CacheableResponsePlugin({ statuses: [200] })],
        })
      );
    
      // Cache the underlying font files with a cache-first strategy for 1 year.
      registerRoute(
        ({ url }) => url.origin === 'https://fonts.gstatic.com',
        new CacheFirst({
          cacheName: 'google-fonts-webfonts',
          plugins: [
            new CacheableResponsePlugin({ statuses: [200] }),
            new ExpirationPlugin({
              maxAgeSeconds: 60 * 60 * 24 * 365,
              maxEntries: 30,
            }),
          ],
        })
      );
    }
    
     

Log in to post a comment.