I Core Web Vitals sono tre metriche con cui Google misura l'esperienza di caricamento e interazione degli utenti reali. Non sono un punteggio estetico né un voto complessivo: sono tre numeri con soglie precise, e ciascuno peggiora per ragioni tecniche indipendenti dalle altre.
La conseguenza pratica è che «ottimizzare la velocità» come attività generica produce spesso poco. Serve sapere quale dei tre numeri è fuori soglia, perché gli interventi che risolvono l'uno non toccano gli altri — e in un caso li peggiorano.
Le soglie#
LCP Largest Contentful Paint quando compare l'elemento più grande ≤ 2,5 s buono ≤ 4,0 s da migliorare > 4,0 s scarsoINP Interaction to Next Paint quanto tarda la risposta a un tocco ≤ 200 ms buono ≤ 500 ms da migliorare > 500 ms scarsoCLS Cumulative Layout Shift quanto il contenuto si sposta da solo ≤ 0,1 buono ≤ 0,25 da migliorare > 0,25 scarsoIl dettaglio che cambia il modo di leggere questi numeri: lavalutazione non usa la MEDIA, usa il 75° PERCENTILE delle visitereali degli ultimi 28 giorni. 100 visite al vostro sito, ordinate dalla più veloce alla più lenta: ┌────────────────────────────────────────────────────────┐ │ ██████████████████████████████████████████████ │ ████ │ │ 75 visite sotto la soglia │ 25 │ └────────────────────────────────────────────────────────┘ ↑ qui viene letto il valoreConseguenza pratica: provare il sito dal proprio portatile su fibramisura il 5° percentile. Il numero che conta viene dai telefoni difascia media su rete mobile, che nella maggior parte dei siti italianisono più della metà del traffico.LCP: nell'ordine giusto#
Le cause dell'LCP, nell'ordine in cui vale la pena controllarle.Saltare l'ordine è il motivo per cui molte ottimizzazioni nonspostano niente. 1. Tempo di risposta del server (TTFB) Se il server impiega 1,2 s a rispondere, l'LCP non può stare sotto 2,5 s per quanto si ottimizzi il resto. Si guarda prima. 2. Risorse che bloccano il disegno Un CSS o uno script sincrono nell'head ferma il parser: finché non è scaricato e interpretato, non compare niente. 3. L'immagine principale scoperta tardi Se l'elemento più grande è un'immagine caricata via CSS, o marcata loading="lazy", il browser la richiede DOPO aver calcolato il layout — cioè secondi più tardi del necessario. 4. I font Se il testo resta invisibile in attesa del font, e il testo è l'elemento più grande, l'LCP aspetta il font.L'ordine non è un'opinione: è una catena di dipendenze. Ottimizzare le immagini quando il server impiega un secondo e due a rispondere è lavoro che non può produrre risultati, perché il collo di bottiglia è a monte.
<!-- L'elemento LCP è l'unico che NON va mai in lazy loading, e l'unico che vale la pena anticipare esplicitamente. --><!-- ✗ Il caso più comune: l'immagine di apertura in lazy. Il browser la richiede dopo il layout, e l'LCP slitta di secondi. --><img src="/foto.webp" loading="lazy"><!-- ✓ Eager, priorità alta, dimensioni dichiarate (che servono anche al CLS: lo spazio è riservato prima che l'immagine arrivi). --><img src="/foto.webp" width="760" height="506" loading="eager" fetchpriority="high" decoding="async" alt="…"><!-- ✓ I font usati sopra la piega: precaricati, così partono in parallelo al CSS invece di aspettarne il download. crossorigin è obbligatorio anche sullo stesso dominio. --><link rel="preload" href="/fonts/titolo.woff2" as="font" type="font/woff2" crossorigin>L'errore più diffuso è il quinto rigo: loading="lazy" applicato a tutte le immagini, compresa quella di apertura. Il caricamento pigro è un ottimo strumento per le immagini sotto la piega; applicato all'elemento LCP fa esattamente il contrario di quello che serve — il browser rimanda la richiesta a dopo il calcolo del layout, e la metrica peggiora di secondi.
I font, che tirano in due direzioni#
/* Il compromesso sui font, che ha effetti opposti su LCP e CLS. */@font-face { font-family: 'Titolo'; src: url('/fonts/titolo.woff2') format('woff2'); /* swap: il testo compare SUBITO con il font di ripiego e viene sostituito all'arrivo. Buono per LCP, rischioso per CLS se i due font hanno metriche diverse (il testo "salta"). */ font-display: swap; /* La correzione: allineare le metriche del font di ripiego a quelle del definitivo, così la sostituzione non sposta niente. */ size-adjust: 97%; ascent-override: 90%; descent-override: 22%;}È il caso in cui LCP e CLS confliggono. font-display: swap fa comparire subito il testo con il font di sistema, il che aiuta l'LCP; ma quando arriva il font definitivo il testo cambia dimensioni e la pagina si assesta, il che peggiora il CLS. Le proprietà di correzione delle metriche — size-adjust, ascent-override — allineano il font di ripiego a quello vero, e la sostituzione diventa invisibile.
INP: una sola causa#
// INP ha una causa dominante sola: attività JavaScript lunghe sul// thread principale. Finché un compito occupa il thread, il browser// non può né rispondere al tocco né ridisegnare.// ✗ Un ciclo su 5000 elementi blocca tutto per centinaia di ms.function elabora(elementi) { for (const e of elementi) calcolaPesante(e);}// ✓ Spezzarlo, restituendo il controllo al browser fra un blocco// e l'altro. Se l'utente tocca qualcosa nel frattempo, il browser// può rispondere.async function elabora(elementi) { for (let i = 0; i < elementi.length; i += 50) { for (const e of elementi.slice(i, i + 50)) calcolaPesante(e); await new Promise(r => setTimeout(r, 0)); }}// ✓ E l'aggiornamento visivo prima del lavoro pesante: l'utente// vede la reazione al proprio tocco entro un frame, il calcolo// avviene dopo.bottone.addEventListener('click', async () => { bottone.classList.add('in-corso'); // riscontro immediato await new Promise(r => requestAnimationFrame(r)); await elabora(dati); // il lavoro vero});L'INP misura il ritardo fra l'azione dell'utente e il primo aggiornamento visibile sullo schermo, considerando le interazioni peggiori della visita. La causa è quasi sempre la stessa: il thread principale è occupato, quindi il browser non può né eseguire il gestore dell'evento né ridisegnare.
La tecnica più efficace, e la meno intuitiva, è la seconda: dare un riscontro visivo prima di cominciare il lavoro. Aggiungere una classe che cambia l'aspetto del pulsante, aspettare un frame, e solo dopo eseguire il calcolo. L'utente vede una reazione entro sedici millisecondi; il fatto che il risultato arrivi dopo mezzo secondo è un'altra cosa, e viene percepita in modo completamente diverso.
CLS: riservare lo spazio prima#
/* CLS: tutto ciò che occupa spazio deve dichiararlo PRIMA, non dopo. *//* ✗ Immagine senza dimensioni: il testo sotto salta quando arriva */img { width: 100%; }/* ✓ Le dimensioni nell'HTML (width/height) o il rapporto nel CSS: il browser riserva lo spazio prima del download */img { width: 100%; height: auto; aspect-ratio: 16 / 9;}/* ✗ Un avviso inserito in cima alla pagina spinge giù tutto il resto *//* ✓ Lo spazio è riservato anche quando l'avviso non c'è, oppure l'avviso è sovrapposto invece che inserito nel flusso */.fascia-avvisi { min-height: 48px; }Il CLS è la più facile delle tre da correggere e la più fastidiosa da subire: è la metrica che misura quante volte il contenuto si è spostato mentre l'utente stava leggendo — o, nel caso peggiore, mentre stava toccando un pulsante che nel frattempo si era mosso.
Laboratorio e campo non misurano la stessa cosa#
# Laboratorio: diagnostica, isola le cause, e' riproducibile.npx lighthouse https://esempio.it --form-factor=mobile \> --only-categories=performance --view# Campo: e' quello che conta davvero. Da Search Console# (Esperienza > Segnali web essenziali) oppure dall'API pubblica:curl -s "https://chromeuxreport.googleapis.com/v1/records:queryRecord?key=$CHIAVE" \> -H 'Content-Type: application/json' \> -d '{"origin":"https://esempio.it","formFactor":"PHONE"}' \> | jq '.record.metrics | {> lcp: .largest_contentful_paint.percentiles.p75,> inp: .interaction_to_next_paint.percentiles.p75,> cls: .cumulative_layout_shift.percentiles.p75 }'{ "lcp": 3800, "inp": 184, "cls": "0.24"}# LCP e CLS fuori soglia, INP a posto: si sa gia' dove intervenire.Un punteggio Lighthouse quasi perfetto insieme a Core Web Vitals non superati sul campo è una combinazione normale, non un errore di misura: significa che gli utenti reali non assomigliano alla simulazione. Il caso tipico è un sito provato su un portatile veloce e visitato da telefoni di quattro anni fa.
Misurare sul proprio pubblico#
// Misurare sul proprio sito, sugli utenti veri, senza terze parti.// L'API è nativa nel browser: bastano poche righe.function segnala(metrica) { navigator.sendBeacon('/api/vitals', JSON.stringify({ nome: metrica.name, // 'LCP' | 'INP' | 'CLS' valore: metrica.value, percorso: location.pathname }));}// Gli osservatori nativi, senza librerie:new PerformanceObserver((l) => { const e = l.getEntries().at(-1); segnala({ name: 'LCP', value: e.startTime });}).observe({ type: 'largest-contentful-paint', buffered: true });// I dati restano vostri, non serve consenso perché non c'è// alcun identificatore dell'utente, e vedete il 75° percentile// del VOSTRO pubblico senza aspettare 28 giorni.Le API sono native nel browser: si possono raccogliere i tre numeri sugli utenti reali senza alcun servizio di terze parti, senza cookie e senza identificatori — quindi senza le implicazioni discusse nell'articolo su cookie e GDPR. Il vantaggio pratico è che si vede l'effetto di una modifica subito, invece di aspettare che la finestra di 28 giorni si aggiorni.
L'ordine degli interventi#
| Metrica | Prima causa da controllare | Intervento più efficace |
|---|---|---|
| LCP | tempo di risposta del server | cache lato server, poi risorse bloccanti |
| LCP | immagine principale in lazy | eager + fetchpriority="high" |
| LCP | CSS bloccante nell'head | un solo foglio, minificato, senza importazioni a catena |
| INP | compiti JavaScript lunghi | spezzarli, e riscontro visivo prima del lavoro |
| INP | gestori che leggono il layout | evitare letture che forzano ricalcoli sincroni |
| CLS | immagini senza dimensioni | width/height o aspect-ratio |
| CLS | font con metriche diverse | size-adjust sul font di ripiego |
| CLS | avvisi inseriti in cima | riservare lo spazio, o sovrapporre |
Una nota conclusiva su cosa aspettarsi: i Core Web Vitals entrano nella valutazione dell'esperienza di pagina, ma non sostituiscono la pertinenza del contenuto. Una pagina velocissima che risponde male alla domanda dell'utente non si posiziona; una pagina lenta con il contenuto migliore parte svantaggiata a parità di tutto il resto. Sono un fattore fra i tanti — con la particolarità di essere uno dei pochi interamente sotto il vostro controllo e verificabile con un numero.