I have a simple service worker, that adds two resources (index.html, app.js) to the cache (on install), deletes old caches (on activate) and serves the resources from cache if present, else from the network (on fetch). To get the new service worker registered and to delete old caches I increase the version number in CACHE_NAME with each version of new resources:
var CACHE_NAME = 'test-cache-v4';
var urlsToCache = ['./','./app.js'];
self.addEventListener('install', function (event) {
event.waitUntil(
caches.open(CACHE_NAME)
.then(function (cache) {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
return self.skipWaiting();
});
self.addEventListener('activate', function(event) {
event.waitUntil(
caches.keys().then(function(keyList) {
return Promise.all(keyList.map(function(key) {
if (key !== CACHE_NAME) {
return caches.delete(key);
}
}));
})
);
});
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.open(CACHE_NAME).then(function(cache){
return cache.match(event.request)
.then(function(response) {
if (response) {
console.log('SERVED FROM CACHE');
return response;
}
return fetch(event.request).then(function(response){
console.log('Response from network is:', response);
return response;
});
}
)
})
);
});
On my localhost the service worker works perfectly fine.
But on the server the response/promise returned by
return cache.match(event.request)
is always undefined. As a result the resources are never served from cache but always from network.
This happens even though in Chrome's dev tools "Application" tab I can see that registration of the new service worker works fine and "Cache Storage" gets filled with my new cache resources (request URLs to index.html and app.js). I also think that I have handled the web server config tweaks correctly with Cache-Control no-cache.
If I change
return cache.match(event.request)
to
return cache.match(event.request.url)
the service worker also works on the server.
However, I would like to understand why the syntax that seems right to me and is used in all the examples I can find does not work on the server.
One difference is that I access localhost directly and the server over https.
EDIT: Another difference is that resources from the server are compressed (Header => content-encoding:gzip).
Could it be linked to one of these differences?
Any ideas are greatly appreciated!
The match() method of the CacheStorage interface checks if a given Request or URL string is a key for a stored Response . This method returns a Promise for a Response , or a Promise which resolves to undefined if no match is found. You can access CacheStorage through the global caches property.
At a high-level, a browser follows the caching order below when it requests a resource: Service worker cache: The service worker checks if the resource is in its cache and decides whether to return the resource itself based on its programmed caching strategies.
The Cache interface provides a persistent storage mechanism for Request / Response object pairs that are cached in long lived memory.
Try to cancel cache mechanism in your server, i found these code in my remote Apache server (served over HTTPS):
<filesMatch "\.(html|htm|js|css)$">
FileETag None
<ifModule mod_headers.c>
Header unset ETag
Header append Vary User-Agent env=!dont-vary
Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
Header set Expires "Wed, 11 Jan 1984 05:00:00 GMT"
</ifModule>
</filesMatch>
My localhost is not served over HTTPS, and the example How to Setup a Basic Service Worker works fine on it.
Cache.match() - Web APIs | MDN shows that parameter should be the Request you are attempting to find in the Cache. But cache.match(event.request.url)
works as well in localhost and remote Apache Server. Caching has no problem.
After test in localhost(no gzip) and remote Apache server(gzip), both caching files without any error. So gzip is not one of the reason.
I got the exactly the same problem: gzipped, server using https, local at localhost.
TL;DR:
return cache.match(event.request, {ignoreVary: true}) // set option `ignoreVary` to `true`
At this page Cache.match() - Web APIs | MDN and I found an option ignoreVary
:
ignoreVary: A Boolean that when set to true tells the matching operation not to perform VARY header matching — i.e. if the URL matches you will get a match regardless of the Response object having a VARY header or not. It defaults to false.
And I checked my request's response, it does have a Vary
field with a value Cookie, Accept-Language
. The cache wouldn't match although the value of this field does not change each time I requested.
So the solution is adding {ignoreVary: true}
to your cache.match()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With