Formattare o minificare: a cosa serve ciascuno e cosa cambia sul peso
Pubblicato il 10/08/2026 · 14 min di lettura · Strumenti per sviluppatori
Daniel Okonkwo — Sviluppatore front-end e redattore Tech presso Allin
Performance web · Formati di file
Verificato su 4 fonti
Formattare serve a leggere; minificare serve a pubblicare. L'argomento del peso è molto più debole di quanto suggerisca il conteggio dei byte grezzi, perché tutto ciò che pubblichi passa da gzip e gzip gestisce già la ripetizione — e l'indentazione è il testo più ripetitivo che esista. Quattro fogli di stile sono passati in questo minificatore, annotando entrambe le dimensioni. Un componente card scritto a mano è passato da 1 612 a 1 118 byte, un taglio del 30,6%; con gzip, da 659 a 555, un risparmio di 104 byte. Separare le due passate è la lezione vera. Sugli stessi quattro file, togliere i soli spazi ha fatto risparmiare 48, 103, 104 e 147 byte compressi — un centinaio, qualunque sia la dimensione, da 1,6 kB a 22 kB. Togliere i soli commenti ne ha fatti risparmiare 57, 1 358, 2 420 e 1 042. Su un file molto commentato i commenti sono il 96% del risparmio reale; gli spazi, un errore di arrotondamento. Minifica quindi per i commenti e il codice morto, non per gli a capo, e mai a scapito della correttezza. Questo minificatore sono cinque espressioni regolari e rompe cinque cose: cancella lo spazio che il CSS richiede ai due lati del + dentro calc(), quindi calc(100% + 16px) diventa una dichiarazione non valida che il browser scarta; modifica l'interno delle stringhe e trasforma content: "a; b" in "a;b"; cancella una stringa che sembra soltanto un commento; si mangia una sequenza di commento dentro un data URI; e riscrive a[title="hello, world"]. Lo stesso sito offre css-compressor, uno scanner che non ne rompe nessuna e pesa 2 byte in più su un file da 22 746 byte.
Quattro fogli di stile veri passati nel minificatore, misurati grezzi e dopo gzip. Togliere tutti gli spazi ha fatto risparmiare 48, 103, 104 e 147 byte compressi; togliere i commenti 57, 1 358, 2 420 e 1 042. E i cinque input che questo minificatore rompe.
La misura che quasi tutti gli articoli saltano: dopo gzip
Un minificatore riporta il conteggio dei byte grezzi perché è l'unico che sa calcolare. Non è quello che viaggia. Ogni foglio di stile servito via HTTP arriva compresso, e le due operazioni si sovrappongono: gzip codifica una sequenza di byte ripetuti come un breve riferimento all'indietro, e quattro spazi di indentazione ripetuti trecento volte sono la cosa più economica che incontrerà mai. Togliere quell'indentazione prima di comprimere elimina un lavoro che gzip faceva gratis.
Le due passate sono quindi state separate e misurate ciascuna per conto suo. Togliere solo i commenti, poi solo gli spazi, poi entrambi, su quattro file: un componente card scritto a mano da 1 612 byte e tre fogli di stile presi così come sono dal repository di questo sito. Compressa con gzip livello 9, la passata degli spazi ha fatto risparmiare 48, 103, 104 e 147 byte. Non percentuali: byte, e all'incirca lo stesso centinaio sia che il file pesasse 1,6 kB sia 22 kB, perché in un foglio di stile c'è solo un numero limitato di indentazioni distinte per quanto lungo diventi. La passata dei commenti, sugli stessi quattro file, ha fatto risparmiare 57, 1 358, 2 420 e 1 042 byte compressi.
Un file basta a dimostrarlo. Il globals.css di questo sito pesa 7 148 byte, buona parte dei quali è prosa che spiega perché ogni colore è stato scelto. Togliere gli spazi lo ha portato da 3 140 byte gzippati a 3 036: 104 byte, il 3,3%. Togliere i commenti lo ha portato a 720, un risparmio di 2 420 byte, il 77%. Il novantasei per cento del guadagno reale veniva dai commenti, e nulla dagli a capo. Ecco tutto l'argomento in un solo file: minifica per togliere ciò che il browser non può usare, non per togliere ciò di cui il compressore già si occupa.
Formattare non costa quasi nulla una volta compresso
La simmetria vale anche nell'altro senso, ed è la parte utile. L'esempio fornito dal formattatore HTML è una pagina minificata da 485 byte. Formattata con indentazione di due spazi diventa 627 byte: 142 in più, un aumento del 29,3%, il numero che mette in agitazione. Con gzip passa da 343 a 369 byte: 26 byte, il 7,6%. Ventisei byte non sono nulla. Se pubblichi una pagina leggibile per un motivo — un esempio di documentazione, un modello di email che qualcuno deve modificare, una pagina di cui vuoi che si possa vedere il sorgente — il costo compresso della leggibilità è inferiore a una richiesta di favicon.
Il formattatore HTML merita anche di essere capito per ciò che si rifiuta di fare. Copia il contenuto di pre e textarea byte per byte, perché i loro spazi vengono resi. Mantiene un singolo spazio fra due elementi in linea, perché toglierlo attaccherebbe due parole sullo schermo. E non tocca un attributo tra virgolette, quindi title="a > b" sopravvive intatto invece di essere tagliato alla parentesi angolare. I tre comportamenti sono stati verificati direttamente e tutti e tre reggono.
Un punto cieco ce l'ha, ed è proprio quello che il suo stesso commento sostiene di aver chiuso. Due pulsanti separati da uno spazio — <div><button>A</button> <button>B</button></div> — escono dal minificatore come <button>A</button><button>B</button>. I pulsanti sono inline-block, quindi quello spazio viene disegnato, e lo stacco fra i due pulsanti sparisce. La regola applicata è che lo spazio fra due tag di blocco è invisibile, e la sua lista di elementi in linea contiene a, b, span, code e un'altra ventina, ma non button, né select, né un'immagine dentro un link. Controlla ogni fila di pulsanti dopo la minificazione.
Cinque input che questo minificatore CSS sbaglia
La pagina css-minifier sono cinque espressioni regolari: togliere i commenti, ridurre le sequenze di spazi a uno solo, eliminare gli spazi attorno a un insieme di segni di punteggiatura, togliere un punto e virgola prima di una graffa di chiusura, rifilare. Basta per un foglio di stile che hai scritto un'ora fa e non basta per nient'altro, perché un'espressione regolare non distingue la struttura dal contenuto.
Il primo fallimento è quello grave. CSS Values and Units Level 3 dice, nella sintassi di calc(), che è richiesto uno spazio da entrambi i lati degli operatori + e -. L'elenco di punteggiatura qui contiene il +, quindi calc(100% + 16px) esce come calc(100%+16px) e l'intera dichiarazione è non valida: il browser la scarta e ripiega su ciò che c'era prima. Poiché il - non è nell'elenco, calc(100% - 16px) sopravvive intatto, il che è peggio — metà delle tue espressioni calc funziona e l'altra metà sparisce in silenzio. Non è un caso inventato. Il file apps/web/components/app/app-shell.module.css di questo repository contiene calc(74px + env(safe-area-inset-bottom)), il riempimento che tiene la barra delle schede mobile lontana dall'indicatore home. Passalo in questo strumento e quel riempimento è sparito.
Le altre quattro nascono dalla stessa radice: le stringhe e i data URI sono contenuto, e le passate li trattano come struttura. content: "a; b" diventa content:"a;b", perché il punto e virgola è nell'elenco di punteggiatura. content: "{ }" diventa content:"{}". a[title="hello, world"] diventa a[title="hello,world"] e il selettore smette di corrispondere. Un foglio di stile la cui stringa content contiene i caratteri che aprono e chiudono un commento CSS — content: "/* not a comment */" — esce come content:"", con la stringa svuotata. E un'immagine di sfondo scritta come data URI che per caso contenga quella stessa sequenza di due caratteri perde tutto ciò che sta in mezzo: url("data:image/svg+xml,...%3E/*x*/%3C...") arriva con il centro cancellato e l'immagine morta.
Due cose che non rompe, per equilibrio. Le proprietà personalizzate sopravvivono: --shadow: 0 2px 8px rgba(0, 0, 0, 0.1) esce come --shadow:0 2px 8px rgba(0,0,0,0.1), lo stesso valore. Sopravvivono anche le media query: @media screen and (min-width: 600px) diventa @media screen and (min-width:600px), ancora valida, e la sintassi di intervallo moderna @media (400px <= width <= 700px) resta intatta, perché <= non è nell'elenco di punteggiatura.
Due minificatori sullo stesso sito, e quello sicuro pesa 2 byte in più
La pagina css-compressor usa un altro motore. Invece di riconoscere schemi, percorre il file carattere per carattere e marca ciascuno come struttura o letterale: tutto ciò che sta dentro una stringa, un commento o una coppia di parentesi è letterale, e nessuna trasformazione può toccarlo. È quell'unico indicatore a preservare un data URI, un'espressione calc() e i decimali di un rgba().
Tutto il senso dell'approccio ingenuo doveva essere il minor peso. Non lo è. Entrambi sono stati eseguiti sugli stessi tre fogli di stile veri. Su app-shell.module.css, 22 746 byte in ingresso, la versione a espressioni regolari ha prodotto 19 278 byte e lo scanner 19 280 — 2 byte di differenza, e con gzip lo scanner era addirittura 1 byte più piccolo, 4 191 contro 4 192. Su admin.css lo scarto è stato di 1 byte grezzo e 2 byte gzippati. Su globals.css, di 8 byte grezzi. Otto byte, su un file dove la versione sicura mantiene lo spazio in @media (min-width: 600px) e in rgba(0, 0, 0, 0.1). Non c'è alcun argomento di dimensione a favore della versione a espressioni regolari; è semplicemente quella che ogni tanto distrugge il file.
L'SVG è l'eccezione, e ha il suo bug
Tutto quanto sopra dice che l'argomento del peso a favore della minificazione è debole. L'SVG è dove è forte, perché il peso di un SVG esportato non è fatto di spazi. Un piccolo disegno salvato da un editor vettoriale si porta dietro una dichiarazione XML, un commento dell'editor, un titolo, una descrizione, un blocco di metadati RDF, una vista nominata con l'ultimo livello di zoom, due namespace dell'editor, una trasformazione a matrice identità, uno spessore di tratto e un'opacità fissati ai propri valori predefiniti, e coordinate scritte con sette decimali. Passa un file simile — 1 071 byte — in svg-optimizer ed esce a 256 byte, un taglio del 76,1%. Con gzip: da 603 a 207, un taglio del 65,7%. Quel risparmio è reale perché il materiale rimosso è testo unico, e il testo unico è esattamente ciò con cui un compressore non può fare nulla.
Quella stessa esecuzione ha fatto emergere un difetto da conoscere prima di usarlo. I colori attraversano due fasi nell'ordine sbagliato: prima vengono accorciati, poi passati alla fase di arrotondamento dei numeri, che legge le cifre esadecimali come numeri. stroke="#000000" viene accorciato in #000 e poi arrotondato in #0. black diventa #0. red diventa #f0. E poiché il modello numerico accetta la notazione scientifica, un colore esadecimale le cui cifre circondano una e esplode: #e5e7eb esce come #e50000000eb, #1e293b come #1e+293b, #0e7490 come #0. La stessa fase riscrive url(#g-0010) in url(#g-10) lasciando intatto id="g-0010", così il gradiente a cui punta sparisce. Tutto questo è stato riprodotto in un browser vero, con le impostazioni predefinite. Disattivare l'opzione di arrotondamento dei numeri evita ogni caso, al prezzo di qualche byte di precisione.
Altri due punti sullo stesso strumento. Rimuove title e desc come metadati dell'editor — ma sono i due elementi che uno screen reader annuncia per un SVG, quindi un'icona accessibile smette di esserlo. Il suo controllo del markup malformato, invece, è di quelli accurati: un browser segnala un errore XML innestando un elemento chiamato parsererror, perciò cercare quel solo nome rifiuterebbe qualsiasi disegno valido che ne porti uno suo. Lo strumento interroga invece il motore: una volta per sessione analizza qualcosa di deliberatamente rotto, legge lo spazio dei nomi del marcatore che torna, e da lì in poi guarda solo là. Un disegno che contiene un elemento parsererror si ottimizza normalmente; un tag non chiuso continua a essere rifiutato.
| File | Originale, grezzo / gzip | Solo spazi, risparmio gzip | Solo commenti, risparmio gzip |
|---|---|---|---|
| Componente card scritto a mano | 1 612 / 659 | 48 byte | 57 byte |
| app-shell.module.css (denso, pochi commenti) | 22 746 / 5 661 | 103 byte | 1 358 byte |
| globals.css (molto commentato) | 7 148 / 3 140 | 104 byte | 2 420 byte |
| admin.css | 4 767 / 1 855 | 147 byte | 1 042 byte |
| SVG esportato da un editor, ottimizzatore completo | 1 071 / 603 | Metadati, non spazi | 396 byte, 65,7% |
Domande frequenti
- Vale ancora la pena minificare il CSS se il server comprime tutto con gzip?
- Sì, ma per i commenti, non per gli spazi. Sui quattro file misurati qui, togliere tutti gli spazi e gli a capo ha fatto risparmiare 48, 103, 104 e 147 byte gzippati — un centinaio di byte ciascuno, sia che il file pesasse 1,6 kB sia 22 kB, perché gzip codifica l'indentazione ripetuta quasi gratis. Togliere i commenti ha fatto risparmiare 57, 1 358, 2 420 e 1 042 byte gzippati sugli stessi file. Se il tuo foglio di stile è documentato, i commenti sono tutto il risparmio; se non lo è, minificarlo ti darà circa un decimo di kilobyte e lo sforzo sarebbe meglio speso in una passata sul CSS inutilizzato. Ciò a cui la minificazione serve davvero, in una catena di build, è che arriva insieme alle passate che contano: togliere le regole che nulla nella pagina usa, unire le dichiarazioni duplicate, accorciare colori e unità.
- Il mio layout si è rotto dopo la minificazione. Dove guardo per primo?
- Cerca calc( nel file minificato e leggili tutti. Se uno contiene un più senza spazi attorno — calc(100%+16px) — quella dichiarazione non è valida e il browser la ignora. Il CSS richiede uno spazio da entrambi i lati di + e - dentro calc(), e un minificatore a espressioni regolari che stringe la punteggiatura lo cancella. Poi cerca content: e ogni selettore di attributo che contenga una virgola o un punto e virgola, perché un minificatore ingenuo modifica l'interno delle stringhe. Guarda quindi i data URI: se uno conteneva i due caratteri che aprono un commento CSS e più avanti i due che lo chiudono, tutto ciò che stava in mezzo è stato rimosso. Infine confronta il numero di regole prima e dopo; se è calato, si è perso qualcosa di strutturale, non solo spazi.
- Devo formattare un file minificato che non ho scritto, prima di modificarlo?
- Sì, ed è l'uso onesto di un formattatore. Riformattare cambia solo gli spazi, quindi non può alterare il comportamento, e trasforma un file su una riga sola in qualcosa che un diff sa descrivere. Due avvertenze. Primo: un file formattato non è automaticamente un file sorgente — se l'originale è stato generato da Sass, TypeScript o una libreria di componenti, modificare l'output significa che la tua modifica sparisce alla build successiva. Secondo: formattare e poi riminificare non è sempre l'identità; sul motore HTML provato qui, un'andata e ritorno aggiunge uno spazio ai due lati del testo dentro ogni elemento non in linea, portando una pagina da 133 byte a 145 e poi restando stabile. Innocuo nel browser, ma non aspettarti indietro un file identico byte per byte.
- Perché l'ottimizzatore SVG fa risparmiare molto più del minificatore CSS?
- Perché rimuove materiale diverso. Un minificatore CSS toglie spazi e commenti; di spazi si occupa già il compressore, quindi contano solo i commenti. Un ottimizzatore SVG toglie metadati dell'editor, attributi predefiniti, gruppi vuoti e decimali superflui — tutto testo unico che un compressore non può ripiegare. Nel file misurato qui, 1 071 byte sono diventati 256 grezzi, e 603 byte gzippati sono diventati 207, un taglio del 65,7% che è sopravvissuto quasi intatto alla compressione. La lezione si generalizza: qualsiasi minificatore che si limiti a riformattare ti deluderà dopo gzip, e qualsiasi minificatore che cancelli contenuto no. Ed è anche per questo che cancellare contenuto è la parte da controllare.
- Brotli è abbastanza diverso da gzip da cambiare la risposta?
- No, la rafforza leggermente. Il foglio di stile della card si è compresso a 527 byte con Brotli contro 659 con gzip, e minificato è sceso a 451 contro 555. Il risparmio assoluto della minificazione è stato di 76 byte con Brotli e di 104 con gzip: un compressore migliore lascia meno da togliere al minificatore, esattamente come ci si aspetta, dato che entrambi rimuovono la stessa ridondanza. Quindi se il tuo hosting serve Brotli — la maggior parte delle CDN lo fa — l'argomento a favore della minificazione degli spazi è ancora più debole, e quello a favore della rimozione dei commenti è invariato, perché un commento è testo unico con entrambi gli algoritmi.
Articoli che potrebbero interessarti
Tutte le guide →Strumenti correlati
Queste cifre vengono dall'aver eseguito questi strumenti su file veri e compresso il risultato, non da una promessa di un fornitore. Il peso dipende interamente dal file: un foglio di stile scritto con commenti lunghi non si comprime come uno senza, e i tuoi numeri non saranno i nostri. Nemmeno i minificatori si equivalgono — due di questo sito divergono sullo stesso input — quindi tratta qualsiasi output minificato come codice nuovo, da rileggere prima di pubblicare. Tieni l'originale leggibile nel controllo di versione, minifica in fase di build e controlla la pagina in un browser prima di pubblicare.
Fonti
- W3C — CSS Values and Units Module Level 3, section 8.1.1 — the syntax of calc(), which states that white space is required on both sides of the + and - operators (the * and / operators may be used without it)
- W3C — CSS Syntax Module Level 3 — the tokenizer: how a string token, a comment and a url() token are recognised, and why a transform that does not run the tokenizer cannot tell a semicolon in a string from a declaration terminator
- IETF — RFC 1952, GZIP file format specification version 4.3 — the DEFLATE-based format used for the Content-Encoding: gzip of every stylesheet measured here, and the back-reference mechanism that makes repeated indentation nearly free
- W3C — Scalable Vector Graphics (SVG) 2 — the title and desc elements and their role in accessible names and descriptions, which is why an optimiser that strips them as editor metadata changes what a screen reader announces
Hai notato un errore in questo articolo?