Perché il tuo sito dovrebbe inviare meno richieste, non richieste più piccole
I file minuscoli non sono gratis. L’HTTP moderno ha ridotto l’overhead delle richieste, non lo ha reso irrilevante.
Indice
- Il mito rassicurante del “basta rendere ogni file più piccolo”
- Le richieste non sono solo byte
- “Ma HTTP/2 ha risolto tutto”, per lo più no
- La waterfall è dove vive la verità
- I file più piccoli contano ancora — solo non allo stesso modo
- I costi nascosti di molti file piccoli
- 1. Scoperta tardiva
- 2. Overhead degli header
- 3. Interruzione del main thread
- 4. Complessità della cache
- Il bundling è tornato, ma con giudizio
- Le richieste di terze parti meritano sospetto extra
- Una checklist pratica per ridurre le richieste
- Rimuovi
- Combina con criterio
- Differisci
- Metti in cache correttamente
- Misura di nuovo
- Com’è un buon risultato
Il mito rassicurante del “basta rendere ogni file più piccolo”
Per anni, i consigli sulle prestazioni web sono sembrati semplici: comprimi tutto, minifica tutto, rendi ogni asset più piccolo.
Quel consiglio resta in gran parte corretto. Uno script da 40 KB di solito è meglio di uno script da 400 KB. Un’immagine ottimizzata è meglio di un export grezzo. Brotli, AVIF, minificazione CSS, tree shaking e font subsetting contano tutti.
Ma su molti siti in produzione, il problema più grande non è più un singolo file sovradimensionato. È il numero di cose che il browser deve richiedere prima che la pagina sembri utilizzabile.
Una pagina può apparire disciplinata nelle dimensioni dei file ed essere comunque lenta perché invia 120 richieste: frammenti CSS, chunk JavaScript, tag di terze parti, file di font, pixel di tracciamento, sprite di icone, endpoint JSON, preload, beacon di analytics e riconvalide della cache. Ognuna può essere “piccola”. Insieme, creano una waterfall lunga e fragile.
La regola pratica è questa: una volta che i singoli asset sono ragionevolmente compressi, ridurre il numero di richieste spesso migliora l’esperienza utente più che limare qualche kilobyte da ogni file.
Le richieste non sono solo byte
Una richiesta di rete non è soltanto un trasferimento di dati. È una sequenza di lavoro.
Il browser deve scoprire la risorsa, decidere quando recuperarla, pianificarla rispetto ad altre risorse, inviare header, attendere il server, ricevere header, analizzare la risposta, spesso decomprimerla e poi farci qualcosa di utile.
Quel “qualcosa di utile” può essere costoso. Un file JavaScript deve essere analizzato, compilato ed eseguito. Un file CSS può bloccare il rendering. Un file di font può ritardare il testo leggibile o causare spostamenti di layout. Un’immagine può influenzare l’elemento Largest Contentful Paint. Uno script di terze parti può portare con sé la propria catena di dipendenze.
Ecco perché il numero di richieste conta ancora anche quando i file sono piccoli. Uno script da 3 KB può essere peggiore di un’immagine da 30 KB se blocca il rendering, arriva tardi ed esegue sul main thread nel momento sbagliato.
Se stai leggendo un report Lighthouse e ti senti punito da decine di avvisi separati, inizia guardando la waterfall delle richieste invece dei singoli punteggi. Abbiamo una guida pratica in come leggere un report Lighthouse senza andare nel panico, ma la versione breve è: trova cosa blocca il primo rendering e cosa ritarda il contenuto principale.
“Ma HTTP/2 ha risolto tutto”, per lo più no
HTTP/2 e HTTP/3 hanno cambiato l’economia delle richieste. Hanno introdotto multiplexing, compressione degli header e un comportamento migliore delle connessioni. In parole semplici, i browser sono diventati molto più bravi a inviare più richieste su meno connessioni.
È stato un miglioramento reale. Ha anche eliminato alcune vecchie abitudini, come gli sprite CSS estremi e i bundle concatenati giganteschi costruiti solo per evitare i limiti di connessione.
Ma HTTP/2 non ha reso gratuite le richieste.
Il multiplexing aiuta quando molte risorse condividono una connessione, ma il browser deve comunque assegnare loro una priorità. I server devono comunque rispondere. Il client deve comunque elaborare ogni risposta. Congestione, perdita di pacchetti, negoziazione TLS, lookup DNS, cache miss e pressione sul main thread esistono ancora.
HTTP/3 migliora alcuni comportamenti di trasporto, soprattutto attorno alla migrazione della connessione e all’head-of-line blocking a livello di trasporto. Non elimina il costo di scoprire, pianificare, scaricare, analizzare ed eseguire risorse.
Quindi l’obiettivo moderno non è “impacchettare tutto in un unico file enorme”. È “inviare meno richieste critiche e rendere intenzionali le richieste rimanenti”.
La waterfall è dove vive la verità
I problemi di prestazioni raramente si annunciano in una singola metrica. Si manifestano come una forma.
Apri il pannello di rete del browser e guarda i primi secondi. Chiediti:
- Quante richieste partono prima che appaia il contenuto principale?
- Quali richieste bloccano il rendering?
- Le risorse importanti vengono scoperte tardi?
- Gli script di terze parti competono con CSS, font o immagini first-party?
- Molti file restituiscono risposte 304 invece di essere serviti direttamente dalla cache?
- Icone, font o frammenti UI sono divisi in più file di quanti ne servano alla pagina?
Una pagina veloce di solito ha una waterfall iniziale noiosa. Un piccolo numero di risorse critiche arriva presto. Le risorse non critiche aspettano. Gli script di terze parti sono ritardati, limitati o rimossi. Il browser non è costretto a gestire venti priorità prima di poter dipingere la pagina.
Una pagina lenta spesso ha una waterfall nervosa: molti file piccoli, molte origini e molte scoperte tardive.
I file più piccoli contano ancora — solo non allo stesso modo
Questo non è un argomento contro la compressione o l’ottimizzazione. È un argomento contro l’ottimizzazione dei byte mentre si ignora il coordinamento.
I file più piccoli contano di più quando la risorsa è grande, blocca il rendering o fa parte del percorso del contenuto principale. Per esempio:
- L’immagine hero dovrebbe essere correttamente dimensionata e codificata.
- Il CSS che blocca il rendering dovrebbe essere snello.
- Il JavaScript necessario per la prima interazione dovrebbe essere minimo.
- I font dovrebbero essere sottoinsiemati, compressi e limitati ai pesi effettivamente usati.
I font sono un esempio comune. I team spesso si fissano sul fatto che un file di font sia da 24 KB o 31 KB, mentre distribuiscono sei pesi, due stili e più famiglie. La soluzione migliore non è togliere 7 KB da un file. È inviare meno file di font. Se la tipografia fa parte del tuo lavoro sulle prestazioni, i web font sono ancora una delle vittorie più facili sulla maggior parte dei siti.
Le immagini seguono lo stesso schema. AVIF o WebP possono risparmiare byte significativi, ma inviare dieci immagini decorative above the fold resta un cattivo piano. Scegli formati migliori, sì, ma chiediti anche se ogni immagine debba essere richiesta. Per le decisioni sui formati, la nostra guida su quando AVIF batte WebP e quando no è un utile complemento a questo lavoro sul numero di richieste.
I costi nascosti di molti file piccoli
Molte richieste piccole tendono a creare problemi che non emergono se guardi solo al totale dei byte trasferiti.
1. Scoperta tardiva
I browser non possono richiedere ciò che non hanno scoperto. Un file CSS può fare riferimento a un font. Uno script può importare un altro script. Un componente può richiedere JSON dopo l’hydration. Ogni dipendenza crea un altro passaggio nella catena.
Più profonda è la catena, più tardi inizia il lavoro importante.
2. Overhead degli header
Ogni richiesta e risposta include header. La compressione degli header aiuta, soprattutto su HTTP/2 e HTTP/3, ma non elimina l’overhead. I cookie possono peggiorare molto la situazione. Se il tuo sito invia cookie grandi con ogni richiesta, gli asset minuscoli diventano meno minuscoli nella pratica.
Questo è uno dei motivi per cui gli asset statici dovrebbero spesso vivere su percorsi o domini senza cookie, e per cui gli header di cache meritano attenzione. Se gli header si comportano in modo strano in produzione, fare debug di redirect e header HTTP è di solito più veloce che tirare a indovinare.
3. Interruzione del main thread
Molti chunk JavaScript possono creare lavoro ripetuto di parsing ed esecuzione. Anche se ogni chunk è piccolo, il browser può continuare a fermarsi per valutare codice. Questo può danneggiare Interaction to Next Paint e far sembrare la pagina scattosa.
All’utente non importa che ogni file fosse piccolo. Gli importa che toccare un menu abbia richiesto 600 millisecondi.
4. Complessità della cache
Suddividere gli asset può migliorare la cache se fatto con attenzione. Un bundle vendor stabile e un bundle app che cambia possono essere una buona separazione.
Ma un chunking eccessivo può ritorcersi contro. Più file significano più lookup in cache, più opportunità di riconvalida, più coordinamento delle versioni e più modi per invalidare accidentalmente risorse che non avevano bisogno di cambiare.
Il bundling è tornato, ma con giudizio
La prima era delle prestazioni web amava il bundling perché i browser avevano limiti di connessione rigidi. Poi è arrivato HTTP/2 e molti team si sono spostati con decisione verso un code splitting aggressivo. Una parte era utile. Una parte è diventata superstizione.
Il punto di equilibrio sensato è il bundling consapevole delle route.
Per un tipico sito marketing o sito di contenuti:
- Inserisci inline o carica solo il CSS necessario al rendering iniziale.
- Mantieni piccolo il JavaScript globale.
- Evita di suddividere moduli minuscoli in richieste di rete separate.
- Ritarda le funzionalità interattive che non servono immediatamente.
- Rimuovi gli script di terze parti che non giustificano il loro costo.
Per un’applicazione:
- Suddividi per route o funzionalità principale, non per ogni componente.
- Mantieni le dipendenze condivise stabili e cacheable.
- Preload solo delle risorse che serviranno sicuramente a breve.
- Evita di caricare codice admin, dashboard, editor o di esperimenti sulle pagine pubbliche.
- Misura il costo di interazione, non solo la dimensione del bundle.
Il bundling non è automaticamente buono. Il code splitting non è automaticamente buono. La domanda utile è: questa separazione aiuta il browser a fornire prima la prossima esperienza utente significativa?
Le richieste di terze parti meritano sospetto extra
Le richieste first-party sono almeno sotto il tuo controllo. Le richieste di terze parti sono spesso più lente, meno prevedibili e più costose di quanto sembrino.
Un singolo tag manager può attivare analytics, annunci, heatmap, widget di chat, A/B testing, strumenti di consenso e script di personalizzazione. Ogni vendor può portare altre richieste. Alcune eseguiranno presto. Alcune bloccheranno il main thread. Alcune cambieranno senza il tuo processo di rilascio.
La migliore ottimizzazione di terze parti è la rimozione. La seconda migliore è il ritardo.
Prima di aggiungere uno script di terze parti, chiedi:
- Deve caricarsi prima che l’utente veda la pagina?
- Deve caricarsi su ogni pagina?
- Può caricarsi dopo consenso, interazione o tempo di inattività?
- Chi ne è responsabile internamente?
- Quale metrica dimostra che vale il costo in termini di prestazioni?
Qui è dove la performance diventa governance. Qualcuno deve essere autorizzato a dire no.
Una checklist pratica per ridurre le richieste
Inizia dalle pagine più importanti: homepage, pagina prezzi, pagina prodotto, checkout, signup o le principali landing page. Poi percorri la waterfall.
Rimuovi
- Elimina JavaScript e CSS inutilizzati.
- Rimuovi vecchi esperimenti, pixel abbandonati e analytics duplicati.
- Elimina pesi di font e librerie di icone non usati.
- Sostituisci le immagini decorative con CSS dove appropriato.
Combina con criterio
- Raggruppa i piccoli moduli JavaScript che si caricano sempre insieme.
- Unisci piccoli file CSS che bloccano lo stesso percorso di rendering.
- Usa sprite SVG o SVG inline per icone ripetute quando riduce le richieste senza danneggiare la manutenibilità.
Differisci
- Lazy-load delle immagini below the fold.
- Ritarda gli script non critici fino a dopo il first paint o l’interazione dell’utente.
- Carica commenti, embed, mappe, chat e player video solo quando servono.
Metti in cache correttamente
- Usa cache a lunga durata per asset statici versionati.
- Evita riconvalide inutili per file che cambiano raramente.
- Mantieni fresco l’HTML, ma lascia che gli asset con hash restino in cache.
Misura di nuovo
Dopo ogni modifica, controlla di nuovo la waterfall. L’obiettivo non è un punteggio perfetto. L’obiettivo è avere meno richieste critiche, rendering utile anticipato e meno disturbo sul main thread.
Com’è un buon risultato
Una pagina sana non ha necessariamente il minor numero possibile di richieste. Ha un percorso critico piccolo e deliberato.
Il browser riceve HTML, CSS essenziale, l’immagine del contenuto principale se ce n’è una, magari un piccolo script necessario per la navigazione o per un’interazione above the fold, e il set minimo di font necessario a rendere il testo leggibile. Tutto il resto aspetta il proprio turno.
Questa è la differenza tra una pagina semplicemente ottimizzata e una pagina che sembra veloce.
Ridurre i file resta utile. Ma se il sito è già ragionevolmente compresso, il prossimo guadagno di prestazioni di solito non è un altro risparmio di 2 KB da un bundle. È una richiesta bloccante in meno, un file di font in meno, uno script di terze parti in meno, una catena di dipendenze in meno.
Meno richieste rendono più semplice il lavoro del browser. Semplice è veloce più spesso di quanto ci piaccia ammettere.