Service worker e cache offline: quando velocizzano il sito e quando lo rallentano

Cosa fa un service worker, quali strategie di cache usare per ogni risorsa, il costo nascosto dell’avvio e il navigation preload, gli errori più comuni e come introdurlo in WordPress.

Un ingranaggio con la sigla SW tra uno smartphone, una nuvola che rappresenta la rete e un archivio che rappresenta la cache

I service worker sono una delle tecnologie più potenti a disposizione di un sito web, e una delle più fraintese. Permettono di far funzionare un sito anche senza connessione, di rendere istantanee le visite successive, di ricevere notifiche. Sono la base delle Progressive Web App. Ma un service worker scritto male, o usato senza una ragione precisa, può rendere il sito più lento, mostrare contenuti vecchi agli utenti e diventare una fonte di problemi difficili da diagnosticare.

In questo articolo vediamo cos’è un service worker, quando porta benefici reali alle prestazioni, quali sono i costi nascosti e come introdurlo senza sorprese in un sito WordPress.

Cos’è un service worker

Un service worker è uno script JavaScript che il browser registra per un sito e che si interpone tra le pagine e la rete. Ogni volta che una pagina del sito chiede una risorsa, un documento HTML, un foglio di stile, un’immagine, una chiamata a un’interfaccia di programmazione, la richiesta passa dal service worker, che può decidere cosa fare: inoltrarla alla rete, rispondere con una copia salvata in una cache dedicata, o combinare le due cose.

Il service worker vive separato dalle pagine: non ha accesso al DOM, gira su un proprio thread e il browser lo avvia quando serve e lo ferma quando è inattivo. Una volta installato, resta attivo anche dopo che l’utente ha chiuso il sito, pronto a intercettare le richieste alla visita successiva. È questa persistenza a renderlo potente, e a rendere importanti gli errori.

La cache usata dal service worker è distinta dalla normale cache HTTP del browser, quella governata dalle intestazioni Cache-Control e dalle risposte 304. La cache HTTP è gestita dal browser secondo regole standard; la cache del service worker è gestita interamente dal codice del sito, che decide cosa salvare, per quanto tempo e quando aggiornarlo.

Le strategie di cache

Il comportamento di un service worker si descrive con alcune strategie, da scegliere per ogni tipo di risorsa.

Tre strategie di un service worker: prima la cache, per file statici con versione come CSS, JavaScript e font, con risposta immediata dalla cache e rete solo se manca; prima la rete, per pagine HTML e dati che cambiano, con cache solo se offline; cache poi aggiorna, per risorse poco critiche, con risposta immediata dalla cache e aggiornamento dalla rete per la volta successiva
Ogni tipo di risorsa richiede una strategia diversa: applicarne una sola a tutto il sito è l’errore più comune.
  • prima la cache: se la risorsa è in cache viene restituita subito, altrimenti si scarica e si salva. È ideale per i file statici con un numero di versione nel nome, come style.3f9a.css, che non cambiano mai: quando cambiano, cambia il nome;
  • prima la rete: si tenta la rete, e solo se fallisce si usa la copia in cache. È adatta alle pagine HTML e ai dati che devono essere aggiornati, con il vantaggio di avere una copia di riserva quando la connessione manca;
  • cache, poi aggiorna: si risponde subito con la copia in cache e intanto si scarica la versione aggiornata per la visita successiva. È un compromesso utile per risorse che cambiano ma per cui una versione leggermente vecchia è accettabile, come avatar o immagini di anteprima;
  • solo rete: il service worker non interviene. È la scelta giusta per le richieste che non devono mai essere servite da una cache, come il carrello, il pagamento, l’area riservata, le chiamate di tracciamento.

Dove migliora davvero le prestazioni

Il beneficio principale riguarda le visite successive. Con i file statici in cache e una strategia che prima cerca in cache, il browser non deve nemmeno verificare con il server se le risorse sono cambiate: le trova localmente e le usa. Per un sito che viene visitato spesso dagli stessi utenti, come un’applicazione web, un gestionale, un servizio in abbonamento, un sito di notizie letto ogni giorno, le visite successive possono diventare molto più rapide, soprattutto su reti mobili instabili.

Il secondo beneficio è la resilienza. Su una connessione intermittente, come in treno o in una zona con copertura scarsa, un service worker può mostrare una pagina salvata o una pagina di cortesia al posto dell’errore del browser. Per alcuni siti, come le applicazioni usate sul campo o i cataloghi consultati in negozio, questa è la ragione principale per adottarlo.

È importante però essere realistici: un service worker non migliora la prima visita, che è quella che conta per molti siti e per gran parte degli utenti che arrivano dai motori di ricerca o dalle campagne. Alla prima visita il service worker non esiste ancora: viene scaricato, installato e attivato, e inizia a intercettare le richieste solo dalle pagine successive. Anzi, se l’installazione scarica subito molti file da salvare in cache, può competere per la banda con le risorse della pagina che l’utente sta guardando.

Il costo nascosto: l’avvio

C’è un costo di cui si parla poco. Quando l’utente apre una pagina del sito, se il service worker non è già in esecuzione il browser deve avviarlo prima di poter gestire la richiesta: caricarne il codice, eseguirlo, attendere che risponda all’evento della richiesta. Su un computer veloce l’avvio richiede pochi millisecondi; su uno smartphone di fascia bassa può richiedere decine o centinaia di millisecondi. Se il service worker poi inoltra la richiesta della pagina alla rete, quel tempo si aggiunge interamente al TTFB.

Confronto illustrativo dei tempi di una navigazione: senza service worker la richiesta HTML richiede 260 millisecondi; con un service worker da avviare si aggiungono 120 millisecondi di avvio prima della richiesta, per un totale di 380; con navigation preload l’avvio e la richiesta avvengono in parallelo e il totale torna a 260 millisecondi
L’avvio del service worker può ritardare la richiesta della pagina; il navigation preload lo evita.

Il paradosso è che un service worker che non fa nulla di utile per le pagine HTML, per esempio uno che si limita a inoltrare tutte le richieste alla rete, peggiora le prestazioni senza offrire alcun vantaggio. È una situazione frequente: plugin o temi che registrano un service worker per le notifiche o per una funzione secondaria, con un gestore delle richieste che intercetta tutto.

Ci sono due rimedi. Il primo è il navigation preload: un’opzione che dice al browser di avviare la richiesta della pagina alla rete in parallelo all’avvio del service worker, così che il tempo di avvio non si sommi. Il service worker riceve poi la risposta già in arrivo e la usa. È la configurazione raccomandata per qualunque service worker che gestisca le navigazioni con una strategia che prima consulta la rete.

self.addEventListener( 'activate', ( event ) => {
	event.waitUntil( self.registration.navigationPreload?.enable() );
} );

self.addEventListener( 'fetch', ( event ) => {
	if ( event.request.mode !== 'navigate' ) {
		return;
	}
	event.respondWith( ( async () => {
		try {
			return ( await event.preloadResponse ) || ( await fetch( event.request ) );
		} catch ( e ) {
			return caches.match( '/offline/' );
		}
	} )() );
} );

Il secondo rimedio è non intercettare ciò che non serve. Un gestore delle richieste che esce subito per le risorse che non deve trattare è molto meglio di uno che le inoltra tutte. Le versioni recenti di Chrome permettono anche di dichiarare, al momento dell’installazione, regole che fanno andare direttamente in rete alcune richieste senza avviare il service worker.

Gli errori più comuni

Mettere in cache l’HTML con la strategia sbagliata. Se le pagine vengono servite prima dalla cache, gli utenti vedono versioni vecchie: un prezzo cambiato, un articolo corretto, un avviso rimosso. Peggio ancora se la pagina contiene informazioni personali, come il nome dell’utente collegato o il numero di prodotti nel carrello, che finiscono in cache e vengono mostrate in modo incoerente. Per l’HTML la strategia più sicura è quasi sempre prima la rete, con la cache come riserva.

Non gestire gli aggiornamenti. Quando si pubblica una nuova versione del sito, il nuovo service worker viene scaricato ma, per impostazione predefinita, entra in funzione solo quando tutte le schede del sito sono state chiuse. Nel frattempo gli utenti usano la versione vecchia, con i file vecchi in cache. Se i file statici non hanno un numero di versione nel nome, si rischia di mescolare CSS nuovo e JavaScript vecchio. La soluzione è dare sempre una versione ai file statici, eliminare le cache delle versioni precedenti all’attivazione e decidere consapevolmente come gestire il passaggio.

Salvare troppo all’installazione. Precaricare decine di file e immagini alla prima visita consuma banda e dati mobili dell’utente, compete con il caricamento della pagina corrente e occupa spazio sul dispositivo. Conviene precaricare solo l’essenziale, registrare il service worker dopo il caricamento della pagina e salvare il resto man mano che viene usato.

Dimenticare di poterlo rimuovere. Un service worker difettoso resta installato nei browser degli utenti anche dopo che il file è stato corretto, finché il browser non verifica gli aggiornamenti. Conviene sapere in anticipo come disattivarlo: pubblicando un service worker che si deregistra e svuota le proprie cache, oppure usando l’intestazione Clear-Site-Data. E conviene servire il file del service worker senza una lunga durata di cache, così che il browser trovi rapidamente le versioni nuove.

Service worker e altre tecniche

Molti dei benefici che un tempo richiedevano un service worker oggi si ottengono con strumenti più semplici. Una buona politica di cache HTTP, con file statici versionati e una durata lunga, fa sì che le visite successive trovino già la maggior parte delle risorse, senza codice aggiuntivo. La back/forward cache rende istantanei i ritorni alle pagine precedenti. Le speculation rules preparano in anticipo le pagine che l’utente probabilmente visiterà. Un service worker ha senso quando serve qualcosa che questi strumenti non danno: il funzionamento offline, il controllo preciso di cosa viene salvato, le notifiche, un’esperienza da applicazione.

Un service worker non impedisce l’uso della back/forward cache e convive con le speculation rules, ma le pagine prerenderizzate passano comunque dal service worker: un gestore lento rallenta anche quelle.

In WordPress

Nei siti WordPress i service worker arrivano di solito da plugin: plugin per le Progressive Web App, per le notifiche push, alcuni plugin di cache e alcuni temi. Prima di tutto conviene sapere se il sito ne ha uno: nei DevTools di Chrome, nella scheda Application, la sezione dedicata mostra i service worker registrati, il loro stato e le cache che usano. Se ce n’è uno, vale la pena capire quale plugin lo registra, quali richieste intercetta e con quale strategia.

Per chi vuole introdurlo, la strada più sicura è partire da una libreria consolidata come Workbox, che implementa le strategie descritte e gestisce versioni e aggiornamenti, e da una configurazione minima: file statici con prima la cache, pagine HTML con prima la rete e navigation preload, una pagina offline, nessuna intercettazione per le aree dinamiche come carrello, pagamento, account e amministrazione.

Come misurare

Gli effetti di un service worker si vedono soprattutto nei dati reali, perché riguardano le visite successive, che i test di laboratorio simulano male. Un sistema di monitoraggio degli utenti reali che distingua le pagine servite con e senza service worker, o le prime visite dalle successive, permette di confrontare TTFB e LCP nei due casi. In laboratorio, il pannello Performance mostra l’avvio del service worker e il tempo speso nel suo gestore; la scheda Rete indica quali risorse sono state servite dal service worker. Un TTFB che peggiora dopo l’introduzione di un service worker è il segnale tipico di un avvio non compensato dal navigation preload.

In sintesi

Un service worker può rendere istantanee le visite successive e far funzionare il sito senza connessione, ma non migliora la prima visita e introduce un costo di avvio che, se non gestito, peggiora il TTFB. Funziona bene quando si scelgono strategie diverse per risorse diverse, si abilita il navigation preload, si evita di intercettare ciò che non serve, si gestiscono versioni e aggiornamenti e si precarica solo l’essenziale. Prima di adottarlo conviene chiedersi se il problema da risolvere non sia già coperto da una buona cache HTTP, dalla back/forward cache o dalle speculation rules; e, se il sito ne ha già uno, verificare che stia aiutando e non rallentando.

IL PROSSIMO PASSO

Partiamo
dal tuo sito.

Raccontaci il progetto, i problemi che riscontri e i tuoi obiettivi. Definiremo insieme un intervento su misura.

Richiedi un’analisi

Ti porta al modulo della pagina Contatti.

MANAGED SERVER S.R.L.info@corewebvitals.it +39 02 5656 9681

Lunedì – venerdì · 09:30 – 19:30

Preferisci un modulo online?
Contattaci su managedserver.it ↗