Vai al contenuto
Web

Performance dei web font: un solo WOFF2, zero spostamenti

Una famiglia non ottimizzata aggiunge 500KB e ritarda il testo leggibile. Subset in WOFF2, self-host e fallback con size-adjust: allo swap nulla si muove.

3 luglio 2026 · 7 min di lettura · Aggiornato il 12 ottobre 2026

A scattered heap of grey files joined by lines into one lit file, which feeds a text panel whose lines sit exactly inside their dashed outlines

Alla fine di questo metodo il sito serve il suo carattere come file WOFF2 con subset, in prima parte, con un fallback tarato in modo che, nel momento in cui arriva il font vero, sulla pagina non si muove niente. È la differenza tra un Largest Contentful Paint sotto 1,5 secondi e uno che scivola oltre i 3 mentre il browser resta bloccato su una richiesta di font.

I font sono uno dei costi di performance più trascurati sul web. Una sola famiglia non ottimizzata aggiunge 500KB a una pagina e ritarda di secondi la comparsa del testo leggibile (web.dev). Dopo i core update di Google del 2026 i Core Web Vitals hanno smesso di fare da spareggio morbido, quindi un font lento o instabile oggi è un costo in ranking e in conversione, non un dettaglio estetico. Ecco la sequenza esatta che seguiamo su ogni SaaS Next.js che pubblichiamo.

Cosa ti serve prima di iniziare

  • I file del font in un formato sorgente (TTF o OTF), o una licenza che ti permetta il self-host. Controlla prima la licenza: non tutte le foundry consentono il self-host.
  • Uno step di build o una CDN che controlli tu per servire gli asset statici.
  • Un modo per misurare: Chrome DevTools, Lighthouse e un tool di dati sul campo (noi usiamo DebugBear o il Chrome UX Report).

Passo 1: verifica cosa carichi davvero

Apri il pannello Network, filtra per font e ricarica. La maggior parte dei siti richiede da quattro a otto file di font e ne usa due. Ogni peso e stile che carichi è un download separato sul percorso critico. Segna le famiglie, i pesi e gli stili che compaiono nel testo davvero renderizzato, non quelli definiti nel CSS per sicurezza. Questa breve lista è l'unica cosa che pubblicherai.

Passo 2: servi WOFF2, e nient'altro

Il WOFF2 usa la compressione Brotli e pesa circa il 30% in meno del vecchio formato WOFF, con supporto in ogni browser uscito negli ultimi anni (web.dev). Nel 2026 non c'è motivo di affiancargli TTF, EOT o WOFF. Un formato, una richiesta per ciascun taglio.

Passo 3: scegli variabile o statico, poi fai il subset

Un font variabile tiene ogni peso e stile in un solo file grazie ai dati di interpolazione, quindi pesa più di un singolo peso statico (circa da 1,5 a 2,5 volte) ma molto meno della collezione che sostituisce. Se il design usa tre pesi o più, un unico WOFF2 variabile da 100 a 200KB di solito batte da 6 a 12 file statici per un totale di 400-800KB, tagliando il payload della metà o più. Se usi esattamente un peso, vince un file statico con subset.

Poi fai il subset. Togli ogni glifo che non renderizzi. Un sito solo in latino non ha bisogno di cirillico, greco o dell'intera gamma di simboli. Fare il subset con uno strumento come glyphhanger, subfont o fonttools riduce un file dell'80% o più. Fai un subset per lingua quando ne servi diverse e lascia che il browser scelga il file giusto con unicode-range.

Passo 4: self-host, in prima parte, con una CDN davanti

Sposta i file su un'infrastruttura che controlli. Il self-host toglie una connessione di terze parti (il lookup DNS, l'handshake TLS e il viaggio verso fonts.gstatic.com che costa un link a Google Fonts) e rende ogni richiesta di font in prima parte. Chiude anche una questione GDPR, perché una CDN di font di terze parti vede gli indirizzi IP dei visitatori, un punto su cui i tribunali tedeschi si sono già pronunciati. Il self-host non vuol dire rinunciare a una CDN: metti la tua CDN davanti all'origine così i file restano in cache vicino agli utenti. L'origine è tua, la CDN è l'acceleratore.

Passo 5: fai il preload dell'unico font above the fold

Aggiungi un solo <link rel="preload" as="font" type="font/woff2" crossorigin> per il taglio che usa il primo testo visibile. Dice al browser di scaricarlo subito invece di scoprirlo in fondo al CSS. Fai il preload di un file, non di cinque: intasare la coda di preload spinge indietro risorse più importanti. L'attributo crossorigin serve anche per i font same-origin, e ometterlo è il motivo più comune per cui un preload non fa niente in silenzio.

Passo 6: imposta font-display per ruolo

font-display controlla cosa mostra il browser mentre un font carica. Applicalo per ruolo, non a tappeto.

  • swap per il peso principale del corpo testo, così il testo è subito visibile in un fallback e passa al font vero quando è pronto. Il testo non resta mai invisibile.
  • optional per un taglio in preload: il browser usa il font solo se arriva quasi all'istante, altrimenti tiene il fallback per tutta la sessione. Così su quel taglio lo spostamento da swap sparisce.
  • fallback per i pesi secondari: un breve periodo di blocco, poi un fallback permanente sulle connessioni lente.

Passo 7: elimina lo spostamento da swap con gli override delle metriche

Il passaggio da fallback a web font provoca spostamento del layout quando i due font hanno metriche diverse. Si risolve nel CSS. Dichiara un @font-face di fallback che punta a un font di sistema locale e correggine le metriche per allinearle al web font con size-adjust, ascent-override, descent-override e line-gap-override. Questi descrittori fanno parte di CSS Fonts Level 4 e sono baseline dal 2023 (MDN). Tarati bene, il fallback occupa lo stesso spazio e lo swap non muove niente. Su Next.js, next/font genera in automatico queste metriche di fallback corrette e ospita i file in self-host in fase di build (documentazione Next.js), coprendo per te i passi 4, 5 e gran parte del 7.

Passo 8: fai una cache seria

I file di font cambiano di rado. Servili con un Cache-Control: max-age lungo (un anno è lo standard) e un hash del contenuto nel nome del file, così una nuova versione invalida la cache in modo pulito. Un visitatore di ritorno non dovrebbe mai scaricare due volte lo stesso taglio.

Come verifichi che funzioni

Ricarica con cache fredda e guarda tre numeri. In Lighthouse, controlla che non compaia l'avviso "Ensure text remains visible during webfont load". Nel pannello Network, controlla un WOFF2 per taglio e un preload che parte presto. Nei dati sul campo (Chrome UX Report o DebugBear), controlla che il Cumulative Layout Shift resti sotto 0,1 e che nessuno spostamento sia attribuito al testo. Se il CLS si muove ancora al caricamento del font, le metriche di fallback sono sbagliate: torna al passo 7.

Errori comuni e come correggerli

  • Il preload non fa niente. Manca l'attributo crossorigin, quindi il browser tratta il preload e la richiesta vera come due download distinti e scarica il font due volte.
  • Il testo lampeggia invisibile (FOIT). font-display è al suo default auto. Metti swap sul taglio del corpo testo.
  • Il font variabile pesa di più, non di meno. Stai servendo un solo peso. Fai il subset di un file statico.
  • Una terza parte rientra di nascosto. Un @import di Google Fonts dimenticato in un foglio di stile riaggiunge la connessione che avevi tolto. Cerca fonts.googleapis.com prima di pubblicare.
  • CLS al caricamento. Le metriche di fallback non sono tarate. Aggiungi size-adjust e i descrittori di override del passo 7.

Per approfondire

I font sono un ingrediente dei Core Web Vitals; per il resto vedi come arriviamo in verde sui Core Web Vitals con Next.js. Gli override delle metriche del passo 7 poggiano sulle funzioni CSS trattate in il CSS moderno nel 2026. E servire i caratteri in prima parte segue la stessa logica di rimuovere Tailwind dalla produzione: meno terze parti, più controllo su cosa va in pagina.

Domande frequenti

Articoli correlati

Studio

Inizia un progetto.

Scriviamo riguardo a ciò che costruiamo. Raccontaci cosa vuoi costruire tu.