HTML, CSS, JavaScript, JSON e SVG sono testo, e il testo si comprime molto bene. Attivare la compressione delle risposte è uno degli interventi con il miglior rapporto tra sforzo e beneficio: spesso basta una riga di configurazione per ridurre il peso trasferito di questi file a una frazione dell'originale. Eppure capita ancora di trovare siti che inviano JavaScript non compresso, o che comprimono le immagini JPEG sprecando CPU senza alcun guadagno.
Come avviene la negoziazione
Il browser dichiara quali algoritmi sa decodificare con l'intestazione Accept-Encoding:
Accept-Encoding: gzip, deflate, br, zstd
Il server sceglie uno di questi, comprime il corpo della risposta e lo segnala con Content-Encoding:
Content-Encoding: br
Vary: Accept-Encoding
L'intestazione Vary è importante: dice alle cache intermedie che la stessa risorsa esiste in versioni diverse a seconda di ciò che il client accetta, così una cache non servirà la versione Brotli a un client che conosce solo gzip.
gzip e Brotli a confronto
| Caratteristica | gzip | Brotli |
|---|---|---|
| Livelli | da 1 a 9 | da 0 a 11 |
| Supporto | universale | tutti i browser moderni, solo su HTTPS |
| Rapporto di compressione | buono | in genere migliore sul testo web |
| Velocità ai livelli alti | rapida | molto lenta ai livelli 10-11 |
| Decompressione | veloce | veloce |
Brotli include un dizionario predefinito con frammenti frequenti di HTML, CSS e JavaScript, e questo lo avvantaggia soprattutto sui file piccoli. Il guadagno rispetto a gzip varia da file a file, ma sul testo web è di solito apprezzabile. Più di recente alcuni browser hanno aggiunto il supporto a Zstandard (zstd), interessante per la sua velocità di compressione; conviene trattarlo come opzione aggiuntiva, mantenendo Brotli e gzip.
Statico e dinamico: livelli diversi
Il punto chiave è distinguere due casi.
File statici: comprimere una volta, al massimo
CSS e JavaScript dei bundle, icone SVG, file di dati che cambiano solo a ogni rilascio possono essere compressi in anticipo, durante la build, al livello massimo. Il tempo di compressione non conta perché si paga una volta sola. Si generano accanto all'originale i file .br e .gz, e il server web li serve direttamente quando il client li accetta.
brotli -q 11 -k dist/app.js # crea app.js.br
gzip -9 -k dist/app.js # crea app.js.gz
Su nginx, per esempio, le direttive gzip_static e, con il modulo apposito, brotli_static dicono al server di cercare la versione precompressa prima di comprimere al volo.
Risposte dinamiche: un livello intermedio
L'HTML generato a ogni richiesta e le risposte delle API vanno compresse al volo. Usare Brotli 11 in questo caso sarebbe un errore: la compressione impiega più tempo di quanto ne faccia risparmiare in trasferimento, e il TTFB peggiora. Per il dinamico si sceglie un livello medio, tipo Brotli 4-6 o gzip 5-6, che offre buona parte del beneficio con un costo di CPU contenuto.
Che cosa non comprimere
Alcuni formati sono già compressi internamente e non traggono vantaggio da un secondo passaggio:
- immagini JPEG, PNG, WebP, AVIF e GIF;
- video e audio (MP4, WebM, MP3, AAC);
- font WOFF2, che usano già Brotli al loro interno;
- archivi ZIP e PDF compressi.
Comprimerli consuma CPU e a volte produce file leggermente più grandi. La configurazione corretta elenca esplicitamente i tipi MIME testuali da comprimere: text/html, text/css, application/javascript, application/json, image/svg+xml, application/xml e simili. Anche il vecchio formato di font TTF beneficia della compressione, mentre WOFF2 no.
Un'altra accortezza: non comprimere risposte minuscole, sotto qualche centinaio di byte, perché l'intestazione dell'algoritmo può annullare il guadagno.
Verificare che funzioni
Ci sono tre modi semplici:
- DevTools, pannello Rete: aggiungere la colonna «Content-Encoding» e confrontare la dimensione trasferita con quella della risorsa;
- riga di comando: chiedere esplicitamente una codifica e leggere le intestazioni;
curl -sI -H "Accept-Encoding: br" "$URL_DEL_FILE" | grep -i content-encoding
- controllo delle cache: verificare che la rete di distribuzione, se presente, non decomprima e invii il file in chiaro, cosa che può succedere con configurazioni errate.
Un errore che si vede spesso
Abilitare la compressione sul server di origine e anche sul proxy o sulla CDN può causare doppia compressione o, più spesso, una cache che conserva una sola variante ignorando Vary. Il sintomo è che alcuni utenti ricevono file illeggibili o non compressi a seconda del primo visitatore che ha popolato la cache. Conviene decidere un solo punto in cui avviene la compressione dinamica e controllarne la configurazione.
In sintesi
- Il testo (HTML, CSS, JS, JSON, SVG) va sempre compresso; immagini, video e WOFF2 no.
- Brotli comprime di norma meglio di gzip sul testo web; tieni gzip come ripiego.
- Statici: precompressione in fase di build al livello massimo.
- Dinamici: livelli intermedi per non peggiorare il TTFB.
- Verifica
Content-EncodingeVaryin DevTools o con curl.