11

When I refresh (or go offline) in Chrome then I get "This site can't be reached" and the following logged to console: The FetchEvent for "http://localhost:8111/survey/174/deployment/193/answer/offline/attendee/240/" resulted in a network error response: a redirected response was used for a request whose redirect mode is not "follow".. When I refresh in Firefox everything works fine. Could someone explain why this is happening?

Here is my simplified SW.

importScripts("/static/js/libs/idb.js")

var CACHE_NAME = "upshot-cache-version3"
var urlsToCache = [...]

self.addEventListener("install", event => {
  event.waitUntil(
    caches
      .open(CACHE_NAME)
      .then(cache => {
        urlsToCache.map(url => cache.add(url))
      })
  )
})

self.addEventListener("activate", event => {
  clients.claim()
})

self.addEventListener('fetch', event => {
  event.respondWith(
    caches
      .match(event.request)
      .then(response => {

        if (response) {
          return response
        }

        var fetchRequest = event.request.clone()

        return fetch(fetchRequest).then(response => {
          if (!response || response.status !== 200 || response.type !== 'basic') {
            return response
          }
          var responseToCache = response.clone()
          caches.open(CACHE_NAME).then(cache => cache.put(event.request, responseToCache))
          return response
        })

      })
  )
})
Nathan Horrigan
  • 763
  • 1
  • 9
  • 20

1 Answers1

21

This is due to a (relatively) recent change in the security restrictions around what kind of responses can be used to satisfy navigations. It should apply to all browsers that support service workers (i.e. both Chrome and Firefox today), but it's possible that you're testing with a version of Firefox that's out of date.

The background on what changed can be found in this issue tracker entry, and there's also more background on the decision that led to the underlying specification.

In terms of modifying your service worker to handle the security restriction, if you're currently responding to navigation requests for certain URLs with a HTTP 30x redirect to a different URL, you'll need to take care not to just store that redirected response directly in the cache.

You can tell whether a given response was redirected by checking whether response.redirected is true, and if so, use code along the lines of this (adapted from the Workbox project) to create a "clean" copy of the response that could then be stored in the cache:

function cleanResponse(response) {
  const clonedResponse = response.clone();

  // Not all browsers support the Response.body stream, so fall back to reading
  // the entire body into memory as a blob.
  const bodyPromise = 'body' in clonedResponse ?
    Promise.resolve(clonedResponse.body) :
    clonedResponse.blob();

  return bodyPromise.then((body) => {
    // new Response() is happy when passed either a stream or a Blob.
    return new Response(body, {
      headers: clonedResponse.headers,
      status: clonedResponse.status,
      statusText: clonedResponse.statusText,
    });
  });
}
Jeff Posnick
  • 53,580
  • 14
  • 141
  • 167
  • Thanks for the detailed response, Firefox version is actually 55.0b13 (Developer Edition). – Nathan Horrigan Aug 01 '17 at 15:02
  • Hmmm... not sure why you would see different behavior, in that case. https://bugzilla.mozilla.org/show_bug.cgi?id=1243792 is the Firefox implementation tracking bug, and the work there is done. – Jeff Posnick Aug 01 '17 at 15:13
  • When implementing this, we are seeing that files are instead being downloaded separately rather than rendered on the page. For example, the .html template doesn't load on the page when the response comes back but instead is sent to our Downloads folder. This appears to be due to the MIME type being octet/stream. Has anyone experienced anything similar to this? – My Stack Overfloweth Feb 27 '19 at 20:28