You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
<!-- pwa --><linkrel="manifest" href="_pwa/manifest.json" /><metaname="theme-color" content="#000000" /><linkrel="apple-touch-icon" href="_pwa/icon-192x192.png" /><script>// register service workerwindow.addEventListener('load',()=>{if('serviceWorker'innavigator){navigator.serviceWorker.register('_pwa/sw.js',{scope: '/'});}});// helper method to get variables from the service worker// example: await getValueFromServiceWorker('VERSION')asyncgetValueFromServiceWorker(key){returnnewPromise(async(resolve)=>{// wait until the controller is ready (this is important especially on first load)while(navigator.serviceWorker.controller===null){awaitnewPromise((resolve)=>setTimeout(()=>resolve(),1000));}navigator.serviceWorker.controller.postMessage({type: 'request-val',key: key});letfn=(event)=>{if(event.data.type==='receive-val'&&event.data.key===key){navigator.serviceWorker.removeEventListener('message',fn,false);resolve(event.data.value);}};navigator.serviceWorker.addEventListener('message',fn);});}// get data from manifestasyncfetchManifest(){returnnewPromise((resolve)=>{fetch(window.location.protocol+'//'+window.location.host+'/app/_pwa/manifest.json').then((res)=>res.json()).then((json)=>{console.log(json);resolve(json);});});}</script><!-- end of pwa -->
create 5 icons
_pwa/icon-192x192.png: transparent icon in size 192x192
_pwa/icon-256x256.png: transparent icon in size 256x256
_pwa/icon-384x384.png: transparent icon in size 384x384
_pwa/icon-512x512.png: transparent icon in size 512x512
_pwa/icon-maskable.png: icon with background (without corners) in size 192x192 with content a little bit shrinked (85%); checked with https://maskable.app/ (minimum safe area / circle)
// configuration optionsself.VERSION=14;// increase number to update the service worker itself (not the assets, they are controlled in the "activate" section)//self.VERSION = Date.now(); // only for debugging reasons(!)letCACHE_NAME='UniqueName'+self.VERSION;letSUBFOLDER='app';// if you really need to import external scripts, you can do something like//self.importScripts('idb-keyval.js'); // copy https://cdn.jsdelivr.net/npm/idb-keyval@6/dist/umd.js to idb-keyval.jsself.addEventListener('install',(event)=>{event.waitUntil((async()=>{// open cacheletcache=awaitcaches.open(CACHE_NAME);// add assets to cache on installation// this has nothing to do with caching assets// this is done below in the cached section of the fetch listenerletassets=[// manifest should also be cached'/_pwa/manifest.json',// icons should also be cached'/_pwa/icon-192x192.png','/_pwa/icon-256x256.png','/_pwa/icon-384x384.png','/_pwa/icon-512x512.png','/_pwa/icon-maskable.png',// all other static assets'/style.css','/script.js','/index.html',// this is needed also'/'];cache.addAll(assets.map(assets__value=>'/'+SUBFOLDER+assets__value));// add offline page (only if you follow the offline-strategy in the fetch event listener)awaitcache.add(newRequest('offline.html',{cache: 'reload'}));})());// replace old service workerself.skipWaiting();});// this is run *NOT* on every page reload(!)self.addEventListener('activate',(event)=>{// new feature: navigation preloadevent.waitUntil((async()=>{if('navigationPreload'inself.registration){awaitself.registration.navigationPreload.enable();}})());// tell the active service worker to take control of the page immediatelyself.clients.claim();});// intercept fetch calls// be aware: this also catches static requests and also the initial page requestself.addEventListener('fetch',(event)=>{// only handle GET requests (never POST, since we want to always do this in the frontend, because we don't want to mess with Requests/Responses)if(event.request.method!=='GET'){return;}// exclude certain dynamic routes from caching (we want to handle the error in the client javascript, not hereelseif(event.request.url.match(/\/api\.php$/)){returnfalse;}// skip chrome extensionsif(!(event.request.url.indexOf('http')===0)){return;}// SPA strategy: always serve shell index when offline (ignoring GET URL parameters)if(event.request.mode==='navigate'&&event.request.headers.get('accept').includes('text/html')){event.respondWith((async()=>{try{letpreloadResponse=awaitevent.preloadResponse;if(preloadResponse){returnpreloadResponse;}letnetworkResponse=awaitfetch(event.request);returnnetworkResponse;}catch(error){letcache=awaitcaches.open(CACHE_NAME);letcachedResponse=awaitcache.match('/'+SUBFOLDER+'/index.html',{ignoreSearch: true});returncachedResponse;}})());}// GET strategy: network first, always update cache, cache fallbackelse{event.respondWith((async()=>{try{letresponse=awaitfetch(event.request),cache=awaitcaches.open(CACHE_NAME);cache.put(event.request,response.clone());returnresponse;}catch(err){letresponse=caches.match(event.request);returnresponse;}})());}// different strategy: always serve offline pageelseif(event.request.mode==='navigate'){event.respondWith((async()=>{try{// try navigation preloadletpreloadResponse=awaitevent.preloadResponse;if(preloadResponse){returnpreloadResponse;}// try networkletnetworkResponse=awaitfetch(event.request);returnnetworkResponse;}catch(error){// if exception (network error), return offline pageletcache=awaitcaches.open(CACHE_NAME);letcachedResponse=awaitcache.match(OFFLINE_URL);returncachedResponse;}})());}});// helper to fetch variables from the service workerself.addEventListener('message',(event)=>{if(event.data.type==='request-val'){self.clients.matchAll().then((clients)=>{clients.forEach((client)=>{client.postMessage({type: 'receive-val',key: event.data.key,value: self[event.data.key]});});});}});
this allows the service worker js to be in a subdirectory
Header add Service-Worker-Allowed /
.htaccess
this disables http caching, but not service worker caching (e.g. for style.css and script.js)
# disable http caching
Header set Cache-Control "no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
Header set Expires 0
# add this if you request from a specific subfolder
<If "%{HTTP_REFERER} =~ m#app#">
Header set Cache-Control "no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
Header set Expires 0
</If>