LCP e immagini di sfondo CSS: il colpevole nascosto del Largest Contentful Paint

L'H1 è piccolo, le immagini hanno i tag giusti, eppure il LCP è ancora 4 secondi. Colpa quasi sempre delle background-image nel CSS: il browser non le pre-carica e le serve solo dopo aver parseato tutto il foglio di stile.
È il caso più frustrante. Hai ottimizzato tutto: l'H1 è testo puro, le immagini sopra la fold hanno width e height, il lazy load è configurato, e il punteggio Lighthouse è ancora sui 70. Il Largest Contentful Paint indica 4 secondi e tu non capisci cosa stia caricando così lentamente. Nov volte su dieci, quando apri la sezione 'LCP element' di Lighthouse, vedi un elemento con una background-image nel CSS.
Il problema non è ovvio perché intuitivamente una background-image sembra uguale a un <img>. Tecnicamente non lo è affatto. E la differenza spiega perché un'immagine di sfondo renderizza quasi sempre più tardi di un tag img equivalente, anche se la risorsa pesa uguale.
Perché il browser non pre-carica le background-image
Quando il browser trova un tag <img> nell'HTML, lo aggiunge subito alla coda di preload del browser — quella che fa partire i download delle risorse critiche in parallelo con il parsing del documento, prima ancora di sapere se e dove verranno usate. È il motivo per cui fetchpriority="high" su un img funziona: l'immagine inizia a scaricarsi non appena il parser la vede.
Le background-image nel CSS non funzionano così. Il browser non sa che un elemento userà una background-image finché non ha scaricato e parseato il foglio di stile, non ha costruito il CSSOM, ha matchato i selettori, e ha calcolato quali regole applicare a quali elementi. Solo a quel punto scopre che serve un'immagine di sfondo e la mette in coda di download. Il risultato: l'immagine parte secondi dopo rispetto a un tag img equivalente, e il LCP si allunga di tutto quel ritardo.
# Verifica se il problema è una background-image: cerca 'largest' nel report
# Lighthouse > Performance > Diagnostics > 'Largest Contentful Paint element'
# Se mostra un <div> o <section> con background-image, quello è il colpevole.
# Da terminale, misura il LCP element con Chrome Headless:
# (richiede puppeteer o lighthouse CLI)
lighthouse https://miosito.com --output=json --quiet \
| jq '.audits["largest-contentful-paint-element"].details.items[0].node.snippet'Soluzione 1: usare <img> con fetchpriority invece di background-image
La soluzione più efficace è la più controintuitiva dal punto di vista del design. Se l'elemento LCP è un'immagine di sfondo di un hero o di una sezione, sostituiscilo con un <img> posizionato in modo assoluto dentro il contenitore. Il browser lo pre-carica, il LCP scende, e l'aspetto visivo è identico:
<!-- Prima: background-image nel CSS (LCP lento) -->
<div class="hero" style="background-image: url('/hero.jpg')">
<h1>Il tuo titolo</h1>
</div>
<!-- Dopo: <img> con fetchpriority (LCP veloce) -->
<div class="hero" style="position: relative; overflow: hidden;">
<img src="/hero.webp"
alt="Descrizione dell'immagine"
width="1920" height="1080"
fetchpriority="high"
decoding="async"
style="position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;" />
<div class="hero-content" style="position: relative; z-index: 1;">
<h1>Il tuo titolo</h1>
</div>
</div>Soluzione 2: preload con <link rel='preload'> quando non puoi cambiare l'HTML
Se per qualche motivo non puoi sostituire la background-image con un <img> (un tema che non puoi modificare, un page builder che la genera via CSS), puoi comunque dire al browser di iniziare a scaricare l'immagine prima con un preload hint nell'head:
<!-- Aggiungi nell'<head>, prima degli altri fogli di stile -->
<link rel="preload"
as="image"
href="/wp-content/uploads/hero.webp"
fetchpriority="high"
type="image/webp" />
<!-- Per immagini responsive con srcset, usa imagesrcset -->
<link rel="preload"
as="image"
href="/hero-desktop.webp"
imagesrcset="/hero-tablet.webp 768w, /hero-desktop.webp 1920w"
imagesizes="100vw"
fetchpriority="high" />Soluzione 3: spostare il CSS critico inline above-the-fold
Se la background-image è in un file CSS esterno che si carica con render-blocking, il ritardo si somma. Una soluzione è inlineare il CSS critico (quello above-the-fold) direttamente nell'HTML, in modo che il browser non debba aspettare il foglio di stile esterno per scoprire che serve un'immagine. I plugin di caching avanzato come WP Rocket oFlying Pages fanno questo in automatico, ma si può anche fare a mano per le sezioni più critiche.
Il punto fondamentale è che il Largest Contentful Paint misura quando l'elemento più grande sopra la fold finisce di renderizzare. Se quell'elemento è un'immagine di sfondo che il browser scopre tardi, il LCP è alto per definizione — non importa quanto sia veloce il resto. Identificare l'elemento LCP è il primo passo; capire perché il browser lo scopre tardi è il secondo. E quasi sempre la risposta è una background-image che aspetta di essere letta nel CSS.
Un audit Core Web Vitals che misura LCP, INP e CLS sugli utenti reali — non su un test di laboratorio — è il modo per capire se il problema è questo o un altro. I numeri del campo raccontano l'esperienza reale, quelli del lab una simulazione. Quando i due non coincidono, il campo vince.
Misura LCP, INP e CLS sui tuoi utenti reali, non su un test di laboratorio.
Un audit Core Web Vitals identifica l'elemento LCP, le risorse che lo rallentano e dove intervenire con priorità. Le background-image nel CSS sono una delle cause più frequenti e meno ovvie.
Articoli correlati

SEO tecnica WordPress: crawl budget, indicizzazione e il mito del 100/100
Leggi
Transient API e database WordPress: quando la cache applicativa diventa un problema
Leggi
