title: Best practices for detecting offline state in a service worker
body: <p>I have a service worker that is supposed to cache an <code>offline.html</code> page that is displayed if the client has no network connection. However, it sometimes believes the navigator is offline even when it is not. That is, <code>navigator.onLine === false</code>. This means the user may get <code>offline.html</code> instead of the actual content even when online, which is obviously something I'd like to avoid.</p>
  
  <p>This is how I register the service worker in my <code>main.js</code>:</p>
  
  <pre><code>// Install service worker for offline use and caching
  if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/service-worker.js', {scope: '/'});
  }
  </code></pre>
  
  <p>My current <code>service-worker.js</code>:</p>
  
  <pre><code>const OFFLINE_URL = '/mysite/offline';
  const CACHE_NAME = 'mysite-static-v1';
  
  self.addEventListener('install', (event) =&gt; {
    event.waitUntil(
      // Cache the offline page when installing the service worker
      fetch(OFFLINE_URL, { credentials: 'include' }).then(response =&gt;
        caches.open(CACHE_NAME).then(cache =&gt; cache.put(OFFLINE_URL, response)),
      ),
    );
  });
  
  self.addEventListener('fetch', (event) =&gt; {
    const requestURL = new URL(event.request.url);
  
    if (requestURL.origin === location.origin) {
      // Load static assets from cache if network is down
      if (/\.(css|js|woff|woff2|ttf|eot|svg)$/.test(requestURL.pathname)) {
        event.respondWith(
          caches.open(CACHE_NAME).then(cache =&gt;
            caches.match(event.request).then((result) =&gt; {
              if (navigator.onLine === false) {
                // We are offline so return the cached version immediately, null or not.
                return result;
              }
              // We are online so let's run the request to make sure our content
              // is up-to-date.
              return fetch(event.request).then((response) =&gt; {
                // Save the result to cache for later use.
                cache.put(event.request, response.clone());
                return response;
              });
            }),
          ),
        );
        return;
      }
    }
  
    if (event.request.mode === 'navigate' &amp;&amp; navigator.onLine === false) {
      // Uh-oh, we navigated to a page while offline. Let's show our default page.
      event.respondWith(caches.match(OFFLINE_URL));
      return;
    }
  
    // Passthrough for everything else
    event.respondWith(fetch(event.request));
  });
  </code></pre>
  
  <p>What am I doing wrong?</p>