Ottimizzazione LCP per Immagini: fetchpriority, Preload e Formati AVIF nel 2026

Il 72% delle pagine mobile ha un'immagine come elemento LCP. Scopri come ottimizzare il Largest Contentful Paint con fetchpriority, preload, AVIF/WebP e immagini responsive — con esempi di codice pronti all'uso.

Perché il Largest Contentful Paint è il collo di bottiglia del tuo sito

Tra le tre metriche Core Web Vitals, il Largest Contentful Paint (LCP) è quella che boccia più siti web al mondo. E non di poco. Secondo i dati del Chrome User Experience Report aggiornati al 2026, solo il 62% delle pagine mobile raggiunge la soglia "buona" di 2,5 secondi — contro il 77% che supera l'INP e l'81% che supera il CLS. Insomma, è proprio l'LCP a trascinare verso il basso il tasso di superamento complessivo dei Core Web Vitals, fermo al 48% su mobile.

Ma ecco il dato che cambia la prospettiva: il 72% delle pagine mobile ha un'immagine come elemento LCP. Non un titolo, non un blocco di testo — un'immagine. Questo vuol dire che per la stragrande maggioranza dei siti, ottimizzare l'LCP significa, nella pratica, ottimizzare il modo in cui carichi le immagini.

In questa guida andiamo a fondo sulle quattro sotto-fasi che compongono l'LCP, vediamo come diagnosticare il vero collo di bottiglia e applichiamo soluzioni concrete — da fetchpriority="high" al preload, dai formati AVIF alle immagini responsive — con codice funzionante che puoi copiare e usare subito nei tuoi progetti.

Le 4 sotto-fasi dell'LCP: dove si nasconde il problema

L'LCP non è un numero monolitico. È la somma di quattro fasi distinte, ognuna con cause e soluzioni diverse.

Capire quale fase sta rallentando il tuo sito è il primo passo — e onestamente, è il passo che la maggior parte degli sviluppatori salta, andando direttamente a comprimere le immagini senza chiedersi se il vero problema sia altrove.

1. Time to First Byte (TTFB)

Il TTFB misura il tempo dal momento in cui l'utente richiede la pagina a quando il browser riceve il primo byte della risposta HTML. Include DNS lookup, connessione TCP/TLS e tempo di elaborazione del server. Per i siti con LCP scarso, il TTFB mediano al 75° percentile è di 2.270 millisecondi — da solo, questo valore quasi satura l'intero budget di 2,5 secondi.

Google raccomanda che il TTFB occupi meno del 40% del budget LCP totale.

2. Resource Load Delay (Ritardo di Scoperta della Risorsa)

È il tempo tra la ricezione del primo byte HTML e il momento in cui il browser inizia effettivamente a scaricare l'immagine LCP. Attenzione: non è il tempo di download — è il tempo di attesa prima che il download cominci. Un dettaglio che fa una differenza enorme.

Ed è qui che si annida un problema che vedo continuamente: il sito mediano con LCP scarso spreca 1,3 secondi prima ancora di iniziare il download dell'immagine. Più della metà del budget di 2,5 secondi, bruciato in una singola sotto-fase. Spesso succede perché l'immagine LCP è nascosta in un background CSS, generata via JavaScript, o semplicemente non è visibile al preload scanner del browser.

Google raccomanda che il Resource Load Delay occupi meno del 10% del budget LCP totale.

3. Resource Load Duration (Durata del Download)

Questa è la fase più intuitiva: il tempo effettivo di download dell'immagine. Dipende dalla dimensione del file, dalla velocità di connessione dell'utente e dalla distanza dal server (o dalla CDN).

Un errore comune è pensare che l'LCP lento sia sempre un problema di "file troppo pesante". In realtà, i dati reali raccontano una storia diversa: per molti siti il Resource Load Delay è il vero collo di bottiglia, non la durata del download. Detto ciò, ridurre il peso dei file resta fondamentale — ed è qui che entrano in gioco formati come AVIF.

4. Element Render Delay (Ritardo di Rendering)

È il tempo tra il completamento del download e il momento in cui l'immagine viene effettivamente dipinta sullo schermo. Qui non c'entra la rete — è un problema del main thread. Se il browser ha scaricato l'immagine ma è impegnato a eseguire JavaScript pesante o a parsare un foglio di stile enorme, l'immagine resta lì, in attesa.

Google raccomanda che l'Element Render Delay occupi circa il 10% del budget LCP totale.

Diagnosticare il collo di bottiglia: workflow pratico

Prima di ottimizzare qualsiasi cosa, devi capire quale delle quattro fasi sta causando il problema. Sembra ovvio, eppure è un passaggio che molti saltano. Ecco un workflow in quattro passi che uso regolarmente.

Passo 1: Identifica l'elemento LCP

Apri Chrome DevTools, vai nella tab Performance e registra un caricamento della pagina. Nella sezione "Timings" troverai il marker LCP — cliccaci sopra per vedere esattamente quale elemento del DOM è stato identificato come LCP.

In alternativa, puoi eseguire un audit Lighthouse e cercare la diagnostica "Largest Contentful Paint element".

Passo 2: Analizza le sotto-fasi con la Performance API

Puoi misurare le sotto-fasi direttamente in JavaScript. Questo snippet è particolarmente utile perché ti dà i numeri esatti di ciascuna fase:

new PerformanceObserver((list) => {
  const entries = list.getEntries();
  const lastEntry = entries[entries.length - 1];

  const navEntry = performance.getEntriesByType('navigation')[0];
  const ttfb = navEntry.responseStart;

  // Le 4 sotto-fasi dell'LCP
  const resourceLoadDelay = lastEntry.resourceLoadTime
    ? lastEntry.startTime - ttfb
    : 0;
  const resourceLoadDuration = lastEntry.resourceLoadTime
    ? lastEntry.responseEnd - lastEntry.startTime
    : 0;
  const elementRenderDelay = lastEntry.renderTime
    - lastEntry.responseEnd;

  console.log('TTFB:', ttfb.toFixed(0), 'ms');
  console.log('Resource Load Delay:', resourceLoadDelay.toFixed(0), 'ms');
  console.log('Resource Load Duration:', resourceLoadDuration.toFixed(0), 'ms');
  console.log('Element Render Delay:', elementRenderDelay.toFixed(0), 'ms');
  console.log('LCP Totale:', lastEntry.renderTime.toFixed(0), 'ms');
}).observe({type: 'largest-contentful-paint', buffered: true});

Passo 3: Controlla il waterfall di rete

Nella tab Network di DevTools, cerca l'immagine LCP. Tre domande chiave da farti:

  • Quando inizia il download? Se parte tardi (dopo CSS e JS), hai un problema di Resource Load Delay
  • Quanto dura il download? Se il file è pesante, il problema è il Resource Load Duration
  • C'è un gap tra fine download e LCP? Se sì, stai guardando un problema di Element Render Delay

Passo 4: Confronta dati di laboratorio e di campo

I dati di laboratorio (Lighthouse, DevTools) ti dicono cosa ottimizzare. I dati di campo (CrUX, Search Console, RUM) ti dicono quanto è grave il problema nella realtà. Servono entrambi: ottimizza in laboratorio, valida sul campo.

fetchpriority="high": la modifica da una riga che può cambiare tutto

L'attributo fetchpriority della Fetch Priority API è probabilmente il miglior rapporto sforzo/risultato nell'ottimizzazione LCP. Una singola riga di codice che dice al browser: "Questa immagine è la più importante della pagina, caricala prima di tutto il resto."

Sembra troppo bello per essere vero? Vediamo come funziona.

Come funziona

Per impostazione predefinita, Chrome assegna priorità Medium alle prime cinque immagini di grandi dimensioni trovate nella pagina. Solo dopo il completamento del layout — cioè dopo aver calcolato quali immagini sono nel viewport — alza la priorità a High per quelle visibili. Ma a quel punto spesso è troppo tardi: l'immagine compete con decine di altre risorse già in coda.

Con fetchpriority="high", il browser assegna priorità alta immediatamente, senza aspettare il layout:

<!-- PRIMA: il browser assegna priorità Medium, poi la alza dopo il layout -->
<img src="/images/hero.avif" alt="Hero banner" width="1200" height="675">

<!-- DOPO: priorità alta immediata, senza attendere il layout -->
<img src="/images/hero.avif" alt="Hero banner" width="1200" height="675"
     fetchpriority="high" loading="eager">

Risultati reali

I numeri parlano da soli:

  • Google Flights: LCP migliorato da 2,6 secondi a 1,9 secondi
  • Etsy: miglioramento dell'LCP del 4% in produzione, con picchi del 20-30% nei test di laboratorio
  • DebugBear: in un caso documentato, LCP passato da 4,2 secondi a 1,9 secondi — più che dimezzato

Regole d'oro per fetchpriority

  • Usa fetchpriority="high" su una sola immagine per pagina — quella LCP
  • Combinalo con loading="eager" (che è il default, ma esplicitarlo evita errori futuri)
  • Non usare MAI loading="lazy" sull'immagine LCP — il 16% dei siti mobile commette ancora questo errore, e fa male vederlo
  • Usa fetchpriority="low" sulle immagini above-the-fold non critiche per ridurre la contesa di banda
<!-- Immagine LCP: massima priorità -->
<img src="/images/hero.avif" alt="Prodotto in evidenza"
     fetchpriority="high" loading="eager"
     width="1200" height="675">

<!-- Immagine above-the-fold ma non LCP: priorità bassa -->
<img src="/images/logo-partner.webp" alt="Logo partner"
     fetchpriority="low"
     width="120" height="40">

<!-- Immagini below-the-fold: lazy loading -->
<img src="/images/gallery-01.webp" alt="Galleria foto 1"
     loading="lazy"
     width="800" height="600">

Preload: scoperta anticipata per immagini CSS e dinamiche

Se l'immagine LCP è un tag <img> direttamente nell'HTML, il preload scanner del browser la scopre subito — nessun problema. Ma ci sono casi (e non sono pochi) in cui l'immagine LCP è invisibile al parser HTML:

  • Immagini di sfondo definite nel CSS (background-image)
  • Immagini caricate via JavaScript (SPA, framework con client-side rendering)
  • Immagini in componenti lazy-loaded da framework come React o Vue

In questi casi, il preload diventa essenziale per anticipare la scoperta della risorsa:

<head>
  <!-- Preload per immagine di sfondo CSS -->
  <link rel="preload" as="image" href="/images/hero-bg.avif"
        fetchpriority="high" type="image/avif">

  <!-- Preload per immagine responsive con media query -->
  <link rel="preload" as="image" fetchpriority="high"
        href="/images/hero-mobile.avif"
        media="(max-width: 768px)"
        type="image/avif">
  <link rel="preload" as="image" fetchpriority="high"
        href="/images/hero-desktop.avif"
        media="(min-width: 769px)"
        type="image/avif">
</head>

Preload con imagesrcset per formati multipli

Se usi il tag <picture> con più formati e dimensioni, puoi sfruttare l'attributo imagesrcset nel preload per indicare al browser l'intera gamma di opzioni disponibili:

<link rel="preload" as="image" fetchpriority="high"
      imagesrcset="/images/hero-480.avif 480w,
                   /images/hero-800.avif 800w,
                   /images/hero-1200.avif 1200w"
      imagesizes="(max-width: 768px) 100vw, 50vw"
      type="image/avif">

Attenzione: non abusare del preload

Fai il preload solo dell'immagine LCP. Ogni preload aggiuntivo sottrae banda alle altre risorse e può peggiorare le performance complessive. Un buon principio da seguire: se il browser può scoprire la risorsa dall'HTML senza aiuto, non serve il preload — basta fetchpriority="high".

Formati moderni: AVIF, WebP e la strategia di fallback

La scelta del formato immagine ha un impatto diretto sul Resource Load Duration, cioè il tempo effettivo di download del file. Con i formati moderni puoi ridurre il peso delle immagini del 50-70% rispetto a JPEG senza perdita percepibile di qualità. E no, non sto esagerando.

AVIF: la compressione più efficiente disponibile

AVIF (AV1 Image File Format) è basato sul codec video AV1 e offre la compressione più aggressiva attualmente disponibile per le immagini web. Netflix ha documentato risparmi del 50% rispetto a JPEG, con picchi oltre il 60% per contenuti 4:4:4.

Le caratteristiche che lo rendono interessante:

  • Compressione lossy e lossless
  • Supporto HDR (High Dynamic Range) e Wide Color Gamut
  • Supporto browser al 92% nel 2026, con baseline "widely available" previsto per luglio 2026
  • Tempo di decodifica nativo nel browser: circa 200-300 ms

WebP: il formato consolidato

WebP offre un risparmio del 25-34% rispetto a JPEG con un supporto browser del 97%. Non comprime quanto AVIF, ma ha un vantaggio non trascurabile: la decodifica è più veloce, e su dispositivi mobile con CPU limitate questo conta eccome.

Il trade-off della decodifica AVIF su mobile

Ecco un aspetto che molti trascurano. AVIF offre i file più piccoli, ma i suoi algoritmi complessi richiedono più CPU per la decodifica rispetto a WebP. Questa operazione avviene sui thread Rasterizer del browser e contribuisce direttamente all'Element Render Delay.

Su dispositivi mobile economici, un file AVIF più piccolo potrebbe scaricarsi più velocemente ma impiegare più tempo a essere dipinto sullo schermo — annullando parzialmente il vantaggio. Non è un caso raro, tra l'altro.

La soluzione? Il tag <picture> con fallback progressivo:

<picture>
  <!-- AVIF: massima compressione per browser compatibili -->
  <source type="image/avif"
          srcset="/images/hero-480.avif 480w,
                 /images/hero-800.avif 800w,
                 /images/hero-1200.avif 1200w,
                 /images/hero-1600.avif 1600w"
          sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw">

  <!-- WebP: ottimo compromesso con supporto quasi universale -->
  <source type="image/webp"
          srcset="/images/hero-480.webp 480w,
                 /images/hero-800.webp 800w,
                 /images/hero-1200.webp 1200w,
                 /images/hero-1600.webp 1600w"
          sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw">

  <!-- JPEG: fallback per browser obsoleti -->
  <img src="/images/hero-800.jpg" alt="Prodotto in evidenza"
       width="1200" height="675"
       sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
       loading="eager" fetchpriority="high"
       decoding="async">
</picture>

Nota l'attributo decoding="async": permette al browser di decodificare l'immagine in modo asincrono, senza bloccare il main thread durante il rendering degli altri elementi.

Immagini responsive: l'attributo sizes che tutti dimenticano

Servire immagini responsive con srcset è fondamentale per evitare di caricare file troppo grandi su dispositivi piccoli. Ma c'è un componente che tantissimi sviluppatori trascurano: l'attributo sizes.

Senza sizes, il browser assume che l'immagine occupi il 100% del viewport. Su un desktop da 1920px scaricherà la versione più grande anche se l'immagine occupa solo metà dello schermo. Uno spreco enorme di banda e tempo di download che si poteva evitare con una riga di codice.

Come impostare sizes correttamente

<!-- Immagine hero: piena larghezza su mobile, metà su tablet, un terzo su desktop -->
<img srcset="/images/hero-480.avif 480w,
            /images/hero-800.avif 800w,
            /images/hero-1200.avif 1200w,
            /images/hero-1600.avif 1600w"
     sizes="(max-width: 768px) 100vw,
            (max-width: 1200px) 50vw,
            33vw"
     src="/images/hero-800.avif"
     alt="Banner promozionale"
     width="1200" height="675"
     fetchpriority="high"
     loading="eager">

Con questa configurazione, un utente su iPhone (viewport ~390px) scaricherà l'immagine da 480w (circa 50-80 KB in AVIF), mentre un utente su desktop a 1920px scaricherà quella da 800w — visto che l'immagine occupa solo il 33% del viewport. Il risparmio di banda può superare il 70% per gli utenti mobile.

Specifica sempre width e height

Definire width e height nell'HTML permette al browser di calcolare l'aspect ratio prima del caricamento dell'immagine, evitando il temuto layout shift (CLS). Un'ottimizzazione che migliora sia LCP che CLS a costo zero.

Ridurre il TTFB: CDN, caching e ottimizzazione server

Se il TTFB è alto, nessuna ottimizzazione delle immagini potrà compensare il ritardo. È un po' come cercare di velocizzare un'auto con le ruote a terra: prima sistema le basi.

CDN: servire le risorse dal nodo più vicino

Una Content Delivery Network serve i contenuti da server distribuiti geograficamente. Se un utente a Milano carica immagini da un server a New York, la latenza di rete aggiunge centinaia di millisecondi a ogni richiesta. Con una CDN, l'immagine viene servita dal nodo più vicino — e la differenza si sente.

Strategia di caching efficace

# Configurazione Nginx per immagini statiche
location ~* \.(avif|webp|jpg|jpeg|png|gif|ico)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
    add_header Vary "Accept";
}

# Configurazione per HTML con stale-while-revalidate
location / {
    add_header Cache-Control "public, max-age=300, stale-while-revalidate=86400";
}

Il pattern stale-while-revalidate è particolarmente efficace: serve immediatamente la versione in cache mentre aggiorna in background. Il risultato? Un TTFB percepito praticamente a zero per le visite successive.

Content negotiation automatica per i formati immagine

Puoi configurare il server per servire automaticamente il formato migliore in base all'header Accept del browser:

# Nginx: content negotiation per formato immagine
map $http_accept $img_suffix {
    default   ".jpg";
    "~image/avif" ".avif";
    "~image/webp" ".webp";
}

location ~* ^(/images/.+)\.(jpg|jpeg|png)$ {
    try_files $1$img_suffix $uri =404;
    add_header Vary "Accept";
}

Con questa configurazione, una richiesta per /images/hero.jpg restituirà automaticamente /images/hero.avif se il browser supporta AVIF, /images/hero.webp per WebP, oppure il JPEG originale come fallback. Tutto trasparente per il frontend.

Eliminare il render-blocking: CSS critico e JavaScript differito

Anche se l'immagine LCP viene scaricata rapidamente, il browser non può dipingerla finché non ha completato il parsing del CSS render-blocking. Un foglio di stile da 200 KB nel <head> può aggiungere centinaia di millisecondi all'Element Render Delay — e a quel punto tutto il lavoro fatto sulle immagini viene vanificato.

Inline del CSS critico

L'idea è semplice: estrai il CSS necessario per il rendering above-the-fold e inseriscilo direttamente nel <head>. Il resto viene caricato in modo asincrono:

<head>
  <!-- CSS critico inline -->
  <style>
    .hero { position: relative; width: 100%; aspect-ratio: 16/9; }
    .hero img { width: 100%; height: auto; object-fit: cover; }
    /* ... solo il CSS above-the-fold ... */
  </style>

  <!-- CSS non critico caricato in modo asincrono -->
  <link rel="preload" href="/css/main.css" as="style"
        onload="this.onload=null;this.rel='stylesheet'">
  <noscript><link rel="stylesheet" href="/css/main.css"></noscript>
</head>

JavaScript differito

Ogni script sincrono nel <head> blocca il rendering. La regola è piuttosto semplice:

  • Script critici: <script defer> — eseguiti dopo il parsing HTML ma prima di DOMContentLoaded
  • Script non critici: <script async> — scaricati in parallelo, eseguiti appena pronti
  • Script di terze parti (analytics, chat, widget): caricati dopo l'evento load o con requestIdleCallback

Ottimizzazione LCP con i framework moderni

Next.js: il componente Image

Next.js offre un componente <Image> che gestisce automaticamente formati, responsive sizing e priorità. Per l'immagine LCP basta aggiungere la prop priority:

import Image from 'next/image';

export default function Hero() {
  return (
    <Image
      src="/images/hero.avif"
      alt="Banner principale"
      width={1600}
      height={900}
      priority          // aggiunge fetchpriority="high" e preload
      placeholder="blur"
      blurDataURL="/images/hero-placeholder.jpg"
      sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
    />
  );
}

La prop priority fa tre cose in una: imposta fetchpriority="high", aggiunge un tag <link rel="preload"> automatico nel <head> e disabilita il lazy loading. Tutto con una singola prop — questo è il tipo di DX che apprezzo nei framework moderni.

Astro: ottimizzazione immagini integrata

Astro include un componente <Image> che ottimizza le immagini a build time, generando automaticamente formati WebP e AVIF:

---
import { Image } from 'astro:assets';
import heroImage from '../assets/hero.jpg';
---

<Image
  src={heroImage}
  alt="Banner principale"
  widths={[480, 800, 1200, 1600]}
  sizes="(max-width: 768px) 100vw, 50vw"
  loading="eager"
  fetchpriority="high"
/>

Checklist completa: ottimizzazione LCP per immagini

Ecco una checklist pratica che riassume tutte le tecniche trattate. Tienila a portata di mano — torna utile più spesso di quanto pensi.

Sotto-fase LCP Obiettivo Azione
TTFB < 40% del budget CDN, caching aggressivo, stale-while-revalidate
Resource Load Delay < 10% del budget fetchpriority="high", preload per immagini CSS/JS, no lazy-load su LCP
Resource Load Duration Minimizzare Formati AVIF/WebP, immagini responsive con sizes, compressione ottimale
Element Render Delay ~10% del budget CSS critico inline, JS differito, decoding="async"

Domande frequenti

Che differenza c'è tra fetchpriority e preload per le immagini?

Sono strumenti complementari che risolvono problemi diversi. fetchpriority="high" aumenta la priorità di rete della risorsa — il browser la scarica prima delle altre — ma non anticipa la scoperta. rel="preload" anticipa la scoperta della risorsa — il browser inizia a cercarla prima di quanto farebbe normalmente — ma la scarica con la priorità di default. Per immagini <img> nell'HTML, basta fetchpriority. Per immagini CSS background o caricate via JS, serve il preload (idealmente con fetchpriority="high").

Devo usare AVIF o WebP per l'immagine LCP?

Dipende dal tuo pubblico. AVIF offre la compressione migliore (50%+ rispetto a JPEG) ma ha una decodifica più pesante su mobile e un supporto browser al 92%. WebP offre compressione buona (25-34% rispetto a JPEG) con supporto al 97% e decodifica più veloce. La strategia migliore? Usare il tag <picture> con AVIF come prima scelta, WebP come fallback e JPEG come ultima risorsa. Così ogni browser riceve il formato ottimale senza compromessi.

Perché non bisogna usare lazy loading sull'immagine LCP?

L'attributo loading="lazy" ritarda intenzionalmente il caricamento dell'immagine fino a quando non è vicina al viewport durante lo scroll. Applicarlo all'immagine LCP — che per definizione è già nel viewport iniziale — aggiunge un ritardo del tutto inutile: il browser deve prima completare il layout, verificare la posizione dell'immagine e solo dopo iniziare il download. Questo errore è talmente comune che il 16% dei siti mobile lo commette ancora.

Quanto migliora l'LCP passando da JPEG a AVIF?

In termini di Resource Load Duration, il risparmio è notevole: AVIF riduce il peso del file del 50-60% rispetto a JPEG a qualità equivalente. Su una connessione 4G tipica, un'immagine hero JPEG da 300 KB diventa un file AVIF da 120-150 KB, con un risparmio di circa 300-500 ms nel tempo di download. Nella pratica, su siti e-commerce la transizione a WebP/AVIF combinata con fetchpriority ha ridotto l'LCP fino al 40%.

Come faccio a sapere qual è l'elemento LCP della mia pagina?

Il modo più rapido è aprire Chrome DevTools, eseguire un audit Lighthouse e cercare la voce "Largest Contentful Paint element" nella sezione Diagnostica. In alternativa, nella tab Performance registra un caricamento, espandi la sezione Timings e clicca sul marker LCP per vedere l'elemento evidenziato nel DOM. Puoi anche usare la Performance API in JavaScript con un PerformanceObserver per il tipo largest-contentful-paint, come mostrato negli esempi di questa guida.

Sull'Autore Editorial Team

Our team of expert writers and editors.