1 14/10/2026 10 min

Quando la compressione gzip smette di funzionare, il sintomo non è sempre evidente: a volte vedi pagine più lente, altre volte il payload esce già compresso ma con header incoerenti, oppure la CPU sale senza alcun beneficio reale. La diagnosi utile non parte dalla direttiva del server, ma dal comportamento osservabile tra browser, proxy, webserver e applicazione.

Il punto di partenza è distinguere tra compressione assente, compressione doppia e compressione parziale. La prima allunga i tempi di trasferimento, la seconda rompe la risposta o genera errori intermittenti, la terza crea casi in cui alcuni contenuti sono compressi e altri no, spesso in base al tipo MIME, al percorso o al layer che risponde per primo.

Capire dove si rompe la catena

Su un webserver moderno la compressione può essere applicata da più livelli: CDN o reverse proxy, webserver front-end, modulo PHP, applicazione, framework, oppure da un servizio intermedio che riscrive gli header. Se non si identifica il layer attivo, si finisce a cambiare configurazioni che non hanno effetto o, peggio, a introdurre doppia compressione.

La verifica minima deve rispondere a tre domande: la risposta porta davvero Content-Encoding: gzip, il body è compatto, e l’header Vary: Accept-Encoding è presente quando serve. Se manca uno di questi elementi, la compressione è incompleta o gestita nel punto sbagliato.

Verifiche rapide con curl e header HTTP

La prova più rapida è una richiesta con negoziazione esplicita dell’encoding. Confronta risposta compressa e non compressa, e controlla gli header prima di toccare la configurazione.

Comando base:

curl -I -H 'Accept-Encoding: gzip' https://example.com/

Atteso in un caso sano: Content-Encoding: gzip sui contenuti testuali, Vary: Accept-Encoding, e nessun header contraddittorio come Content-Length incoerente con il corpo ricevuto dopo decompressione lato client.

Per vedere il corpo trasferito e capire se il server sta davvero comprimendo, usa una richiesta verbosa:

curl -s --compressed -D - https://example.com/ -o /dev/null

Se --compressed non mostra Content-Encoding: gzip ma il sito dovrebbe comprimerlo, il problema è a monte: modulo non caricato, regola esclusa, tipo file non incluso, oppure un proxy che rimuove l’header.

Se invece l’header compare ma il browser segnala risposta corrotta o il contenuto appare vuoto, sospetta doppia compressione o body troncato. In quel caso la differenza tra layer è decisiva: il proxy potrebbe aver compresso e l’applicazione aver provato a farlo di nuovo.

Diagnosi per layer: CDN, proxy, webserver, applicazione

La catena va letta dall’esterno verso l’interno. Se hai una CDN, verifica prima gli header restituiti dall’edge. Molte CDN comprimono automaticamente HTML, CSS, JS e JSON, ma lasciano intatti immagini e archivi. Se il webserver originario comprime ancora gli stessi tipi, il rischio di conflitto aumenta.

Una distinzione utile è questa: se curl verso l’origin mostra gzip corretto ma il dominio pubblico no, il problema è nell’edge o nel reverse proxy. Se invece l’origin stesso non comprime, il guasto è locale al webserver o all’applicazione.

Quando il front-end è Nginx, la compressione è spesso gestita con gzip on; e con l’elenco dei MIME consentiti. Con Apache, la parte critica è il modulo mod_deflate e le eventuali esclusioni per percorso o tipo contenuto. Con PHP-FPM, la compressione non dovrebbe essere affidata al runtime se il webserver la gestisce già, salvo casi precisi e documentati.

Cause frequenti e come falsificarle in pochi minuti

1. Il modulo o la direttiva non sono attivi. È la causa più banale. Su Nginx controlla la configurazione effettiva e la presenza di gzip on;; su Apache verifica che mod_deflate sia caricato. Falsificazione rapida: cerca la direttiva attiva e ricarica la configurazione senza cambiare altro.

nginx -T 2>/dev/null | grep -nE 'gzip|brotli|gzip_types|gzip_min_length'
apachectl -M | grep -E 'deflate|brotli'

2. I tipi MIME non sono inclusi. Molti ambienti comprimono solo text/html e dimenticano application/json, application/javascript, text/css. Falsificazione rapida: chiedi un file statico noto e controlla se il tipo è tra quelli abilitati nella configurazione.

curl -I -H 'Accept-Encoding: gzip' https://example.com/app.js

3. Un proxy o una CDN riscrive gli header. In questo caso il server origin è corretto, ma il client non vede la compressione o vede un header incoerente. Falsificazione rapida: confronta una richiesta diretta all’origin con una al dominio pubblico, usando host header o IP temporaneo se consentito.

curl -I --resolve example.com:443:ORIGIN_IP -H 'Accept-Encoding: gzip' https://example.com/

4. La compressione è attiva ma il contenuto è già compresso o troppo piccolo. Immagini, PDF, ZIP e file inferiori alla soglia non dovrebbero essere compressi. Se la configurazione forza gzip su tutto, sprechi CPU senza guadagno e puoi introdurre errori. Falsificazione rapida: confronta un HTML grande e un asset binario.

Un altro indizio pratico è il tempo di risposta. Se la CPU cresce ma il TTFB non migliora, potresti stare comprimendo contenuti che non ne beneficiano oppure farlo in un punto sbagliato della catena. La metrica da guardare è il rapporto tra dimensione trasferita e tempo di risposta, non solo l’uso CPU in astratto.

Configurazione corretta su Nginx

Su Nginx la compressione va tenuta semplice. La configurazione deve essere esplicita, limitata ai tipi testuali, e con una soglia sensata. Un esempio minimale:

gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
gzip_min_length 1024;
gzip_types text/plain text/css application/javascript application/json application/xml image/svg+xml;

Prima di applicarla, verifica che non esista già una configurazione equivalente in un file incluso altrove. Il rischio tipico è duplicare direttive in server o location diversi, con comportamento incoerente tra URL statici e dinamici.

Check operativo:

nginx -t

Se il test è pulito, il reload è il passo reversibile corretto:

systemctl reload nginx

Se il sito è dietro proxy o CDN, non alzare troppo il livello di compressione senza motivo. Il guadagno di pochi punti percentuali non giustifica il costo CPU su pagine ad alto traffico. In molti casi un livello medio è il compromesso giusto.

Configurazione corretta su Apache

Con Apache il controllo passa da mod_deflate. Una configurazione frequente e pulita usa AddOutputFilterByType per i contenuti testuali e lascia fuori i binari. Esempio:

<IfModule mod_deflate.c> AddOutputFilterByType DEFLATE text/html text/plain text/css text/javascript application/javascript application/json application/xml application/xhtml+xml text/xml image/svg+xml Header append Vary Accept-Encoding
</IfModule>

La presenza di Header append Vary Accept-Encoding è importante quando la risposta può essere servita in varianti compressa e non compressa. Senza questo header, cache intermedie e browser possono memorizzare una variante sbagliata.

Controllo moduli:

apachectl -M | grep -E 'deflate|headers'

Se il modulo non è caricato, il fix corretto è abilitarlo con il meccanismo della tua distribuzione, poi fare reload e ricontrollare gli header:

systemctl reload apache2

Su ambienti con Multi-Processing Module specifico o hosting condiviso, il percorso può cambiare. In quel caso la verifica migliore è sempre sulla configurazione effettiva in uso, non sul file che pensi dovrebbe essere attivo.

Quando gzip funziona ma sembra lento

Un errore comune è interpretare la compressione come soluzione universale alle performance. In realtà gzip riduce i byte trasferiti, non la latenza complessiva in ogni scenario. Se il collo di bottiglia è il backend, il database o il rendering lato applicazione, comprimere meglio non risolve il problema di fondo.

La metrica da osservare qui è il TTFB e, se hai tracing, il tempo speso tra risposta origin e invio al client. Se la CPU del webserver sale e il TTFB non scende, la compressione è probabilmente un costo aggiuntivo senza beneficio sufficiente.

Per misurare il differenziale, confronta una richiesta con e senza Accept-Encoding e osserva time_starttransfer e size_download:

curl -o /dev/null -s -w 'TTFB=%{time_starttransfer} size=%{size_download}
' https://example.com/
curl -o /dev/null -s -H 'Accept-Encoding: gzip' -w 'TTFB=%{time_starttransfer} size=%{size_download}
' https://example.com/

Se la dimensione cala ma il TTFB peggiora in modo evidente, conviene rivedere livello di compressione, elenco MIME e posizione del layer che comprime. In certi carichi è meglio comprimere solo HTML e JSON, lasciando fuori asset che non portano vantaggio reale.

Compressione doppia: il guasto più fastidioso

La doppia compressione nasce quando due livelli applicano gzip allo stesso body. Succede spesso con CDN che comprimono automaticamente e origin che fa lo stesso, oppure con applicazione e webserver entrambi configurati per compressione dinamica.

Il sintomo tipico è un errore lato browser, un body incomprensibile o un comportamento inconsistente tra endpoint. La verifica utile è confrontare gli header e capire chi è l’ultimo layer che tocca la risposta. Se vedi Content-Encoding: gzip ma il contenuto non si apre correttamente, sospetta una seconda compressione o un proxy che ha alterato il flusso.

La correzione non è “disattivare tutto”, ma assegnare la responsabilità a un solo punto della catena. Se la CDN comprime, l’origin deve servire contenuto non compresso ma cache-friendly. Se il webserver comprime, la CDN deve essere configurata per non ricomprimere ciò che riceve già compresso.

Cache, Vary e contenuti dinamici

Un aspetto che viene trascurato è l’interazione con la cache. Se un reverse proxy memorizza la variante compressa senza rispettare Vary: Accept-Encoding, alcuni client riceveranno una risposta sbagliata. Il bug può sembrare casuale perché dipende dall’ordine delle richieste e dal comportamento del cache layer.

Verifica minima:

curl -I -H 'Accept-Encoding: gzip' https://example.com/ | grep -iE 'vary|content-encoding|cache-control'

Se il tuo stack usa un proxy caching, controlla che la policy distingua correttamente tra varianti compresse e non compresse. Il fix corretto è nel layer di cache, non nel file PHP o nel template HTML.

Per i contenuti dinamici, evita di forzare gzip su risposte già piccole o altamente variabili. In un sito con molte risposte personalizzate, la compressione può essere utile solo su pagine lunghe, feed, JSON voluminosi e asset testuali statici.

Checklist operativa prima di cambiare qualcosa

  1. Controlla gli header con curl -I -H 'Accept-Encoding: gzip' sul dominio pubblico e, se possibile, sull’origin.
  2. Verifica Content-Encoding, Vary, Content-Type e Content-Length.
  3. Conferma quale layer comprime davvero: CDN, proxy, webserver o applicazione.
  4. Escludi doppia compressione confrontando origin ed edge.
  5. Misura TTFB e dimensione trasferita prima e dopo ogni modifica.
  6. Applica una sola modifica alla volta e ricarica il servizio, non riavviarlo se non serve.

Rollback e blast radius

Ogni modifica alla compressione ha un blast radius ampio: può toccare cache, CDN, payload API e compatibilità con client vecchi o proxy intermedi. Per questo il rollback deve essere banale: ripristino del file di configurazione precedente, reload del servizio e nuova verifica con curl.

Prima di cambiare, salva sempre la configurazione attuale. Su Nginx e Apache basta una copia o un diff chiaro del file coinvolto:

cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak.$(date +%F_%H%M)

Oppure, meglio ancora, lavora con snippet versionati. Se il problema nasce dopo un deploy, il rollback più sicuro è tornare alla revisione precedente della configurazione o disabilitare temporaneamente la direttiva introdotta di recente.

Se il guasto è su produzione e il sito è lento ma non rotto, la mitigazione minima è ridurre il perimetro della compressione: lascia gzip solo su HTML e JSON, escludi asset già compressi e verifica che la CDN non ricomprima. Questo cambia poco il comportamento visibile ma riduce il rischio di regressioni.

Assunzione: l’istanza è Linux recente, con webserver dietro eventuale CDN/reverse proxy, e la compressione è gestita dal layer applicativo o dal front-end HTTP.