Niveau Niveau expert

Cache et compression des pages web avec Gzip ou Deflate en HTTP

Articledéveloppement

Publié par le , mis à jour le (218908 lectures)

développement serveur performance compression cache gzip deflate

Intérêt de la compression

De nos jours, l'accent est souvent mis sur la performance des sites et les économies de bande passante notamment sur mobile. Les temps d'accès et de téléchargement se mesurent (très précisément avec de nombreux outils) en millisecondes. De nombreuses recommandations provenant de Google et Yahoo font mention de la compression des pages avant leur transit sur le réseau.

  1. Le serveur compresse les données (code HTML, CSS, JavaScript...)
  2. Les fichiers transitent par le réseau via HTTP
  3. Le navigateur décompresse à la volée les données avant de les interpréter

Tar Gzip

Ce qui représentait une charge supplémentaire pour les serveurs web à l'époque où leur puissance était moindre, peut désormais devenir négligeable en regard des améliorations apportées, notamment pour les navigateurs mobiles. Si la compression impose une charge trop importante à votre serveur, il est possible de pré-compresser les contenus, les placer en cache et les délivrer directement.

Ces techniques qui sont prévues depuis HTTP/1.1 (1999) peuvent tout à fait être mises en œuvre pour les documents HTML mais aussi CSS, XML ou JavaScript. Il est inutile de s'en servir pour les fichiers binaires (images, vidéos, PDF...). Elles ne vous dispensent pas de réduire initialement la taille des fichiers HTML ou CSS (les "minifier") en appliquant d'autres critères tels que la suppression des espaces excédentaires ou des commentaires inutiles.

Zip

Navigateurs

La première interrogation - dans le monde impitoyable de la création web et des guerres entre navigateurs modernes et antiques - concerne le support de cette fonctionnalité. Or, ici, bonne nouvelle : on peut considérer que la totalité des navigateurs supportent la décompression des pages avec HTTP/1.1 :

  • Microsoft Internet Explorer depuis 4.0*
  • Opera depuis 5.12
  • Firefox toutes versions
  • Google Chrome toutes versions
  • Safari toutes versions
  • Netscape depuis 4.06
  • Tous les navigateurs mobiles

* avec quelques petits bugs jusqu'aux versions 5.0 et 6.0 comprises

De plus, il incombe aux navigateurs d'envoyer un en-tête HTTP indiquant les types de pages compressées supportées . Si cet en-tête ne figure pas dans ceux reçus par le serveur, il lui suffit de ne pas activer la compression.

GET / HTTP/1.1
Host: www.alsacreations.com
Accept-Encoding: gzip
User-Agent: Firefox/3.6

Le serveur répond alors de la même manière, grâce à Content-Encoding, et en faisant suivre par le contenu compressé de la page.

HTTP/1.1 200 OK
Server: Apache
Content-Type: text/html
Content-Encoding: gzip
Content-Length: 13337

...

Deux formats coexistent :

  • Deflate, algorithme qui couple LZ77 et le codage de Huffman (zlib)
  • Gzip, évolution de Deflate, un peu plus performant, mieux supporté, plus répandu

Mise en place au niveau des serveurs web

Logo Apache

Attention : les indications suivantes doivent être ajustées selon votre configuration et vos besoins.

Apache est équipé de modules de compression :

  • dans les anciennes versions 1.3 : mod_gzip ou mod_deflate
  • depuis la version 2.0 et supérieures : du module officiel mod_deflate qui utilise zlib et remplace mod_gzip

Ces modules sont désactivés par défaut, mais peuvent être activés dans la configuration générale du serveur si vous y avez accès. Par défaut mod_deflate permet de spécifier les types de fichiers à compresser à la volée grâce à la directive AddOutputFilterByType DEFLATE. Une fois ces modules disponibles vous pouvez également exploiter les fichiers .htaccess dans chaque répertoire pour plus de souplesse (voir au point suivant).

Activation des modules pour Apache 2

Si votre serveur le permet, vous devez accéder en ligne de commande avec les droits root pour activer les modules nécessaires avec la commande a2enmod :

a2enmod headers
a2enmod deflate
/etc/init.d/apache2 restart
# ou... service apache2 restart

Manuellement en ajoutant aussi un fichier dans mods-available/deflate.load et un lien symbolique vers celui-ci dans mods-enabled/, ou une ligne dans httpd.conf :

LoadModule deflate_module /usr/lib/apache2/modules/mod_deflate.so

Sous Windows, il faudra indiquer le chemin du fichier .dll éponyme. Attention : si un lien symbolique vers mod_deflate.conf est déjà présent dans mods-enabled avec une directive de configuration générale, il est possible que tous vos fichiers soient déjà délivrés compressés. Faites un test avant tout - voir en fin d'article.

Puis il faut ajouter des directives à la configuration (par exemple dans un fichier situé dans /etc/apache2/conf.d/) pour compresser des types de fichiers bien spécifiques, dans un répertoire spécifique lui aussi. Ceci est recommandé lorsque l'on place toutes les feuilles de style dans un répertoire indépendant, ainsi que les JavaScripts, car le but n'est pas de (re)compresser tous les fichiers hébergés sur le serveur web mais de se focaliser sur l'essentiel. Il est donc possible d'indiquer <Location /css> pour n'appliquer ces règles que sur ce répertoire (on considère ici l'URL) et ses descendants, ou d'utiliser <Directory /chemin/absolu/vers/css> si l'on se réfère au système de fichiers.

<IfModule mod_deflate.c>
 SetOutputFilter DEFLATE
 DeflateCompressionLevel 9
</IfModule>

<Location />
 AddOutputFilterByType DEFLATE text/plain
 AddOutputFilterByType DEFLATE text/xml
 AddOutputFilterByType DEFLATE text/html
 AddOutputFilterByType DEFLATE text/css
 AddOutputFilterByType DEFLATE image/svg+xml
 AddOutputFilterByType DEFLATE application/xhtml+xml
 AddOutputFilterByType DEFLATE application/xml
 AddOutputFilterByType DEFLATE application/rss+xml
 AddOutputFilterByType DEFLATE application/atom_xml
 AddOutputFilterByType DEFLATE application/x-javascript

 # Pour les proxies
 Header append Vary User-Agent env=!dont-vary
</Location>
DeflateCompressionLevel
Indique le facteur de compression, de 1 (faible, par défaut) à 9 (fort) : n'est applicable qu'au niveau de la configuration globale du serveur, pas dans les fichiers .htaccess
AddOutputFilterByType DEFLATE text/html
Applique la compression sur les fichiers de type mime text/html
SetOutputFilter DEFLATE
Active le filtre compression.

N'oubliez pas de redémarrer (restart) ou recharger (reload) Apache après chaque modification de la configuration.

/etc/init.d/apache2 restart
# ou service apache2 restart

Apache 1.3

Pour activer le module dans le fichier de configuration httpd.conf, ajoutez la ligne :

LoadModule gzip_module modules/mod_gzip.so

Puis configuration, semblable à celle mentionnée auparavant :

<IfModule mod_gzip.c>
   mod_gzip_on Yes
   mod_gzip_can_negotiate Yes
   mod_gzip_static_suffix .gz
   AddEncoding gzip .gz
   mod_gzip_update_static No
   mod_gzip_command_version '/mod_gzip_status'
   mod_gzip_temp_dir /tmp
   mod_gzip_keep_workfiles No
   mod_gzip_minimum_file_size 500
   mod_gzip_maximum_file_size 500000
   mod_gzip_maximum_inmem_size 60000
   mod_gzip_min_http 1000
   mod_gzip_handle_methods GET POST
   mod_gzip_item_exclude reqheader "User-agent: Mozilla/4.0[678]"
   mod_gzip_item_include file \.html$
   mod_gzip_item_include file \.htm$
   mod_gzip_item_include file \.php3$
   mod_gzip_item_include file \.php$
   mod_gzip_item_include file \.js$
   mod_gzip_item_include file \.css$
   mod_gzip_item_include mime ^text/
   mod_gzip_item_exclude mime ^httpd/unix-directory
   mod_gzip_item_exclude mime ^image/
   mod_gzip_dechunk Yes
   mod_gzip_add_header_count Yes
   mod_gzip_send_vary Yes
</IfModule>

N'oubliez pas de recharger Apache après chaque modification de la configuration.

Les options sont relativement parlantes (pour les anglophones), elles ne seront pas détaillées dans cet article.

Microsoft IIS

IIS supporte la compression depuis la version 4, mais celle-ci est victime de bugs. Dans la version 5, les efforts de Microsoft n'ont pas porté leurs fruits puisque celle-ci est toujours instable. C'est enfin dans la version 6 que la compression HTTP a été finalisée. Cependant elle nécessite quelques manipulations (voir documentation Microsoft : Using HTTP Compression for Faster Downloads (IIS 6.0) et tutoriel en français pour activer la compression GZip dans IIS6.

Les autres serveurs restent marginaux. Lighttpd est équipé du module bien nommé mod_compress.

Solutions rapides avec un .htaccess pour Apache

Les fichiers .htaccess sont des fichiers placés à la base d'un répertoire et modifiant le comportement du serveur pour les fichiers qu'il contient. On peut y placer les instructions de configuration mentionnées ci-dessus (sans la directive Location ou Directory).

Voici des exemples testés et exploités sur Alsacreations.com. Si vous obtenez des erreurs HTTP 500 après la mise en place du fichier .htaccess, vérifiez sa syntaxe, l'adéquation avec votre type de serveur et la disponibilité des modules. Vous pouvez également combiner le tout avec des options de cache (mod_expires dans l'exemple pour Apache 1.3) pour éviter de servir plusieurs fois le même contenu aux visiteurs et sa compression par le serveur - ceci relève d'un autre article.

Apache 2

Contenu du fichier .htaccess, dans le répertoire contenant les fichiers CSS et JavaScript.

# Apache 2.0
SetOutputFilter DEFLATE
AddOutputFilterByType DEFLATE text/html text/css text/plain text/xml application/x-javascript

Apache 1.3

# Compression pour fichiers CSS
<IfModule mod_gzip.c>
    mod_gzip_on       Yes
    mod_gzip_dechunk  Yes
    mod_gzip_minimum_file_size 1024
    mod_gzip_maximum_file_size 100000
    mod_gzip_item_include file \.css$
    mod_gzip_item_include mime ^text/css$
</IfModule>

<IfModule mod_expires.c>
    ExpiresActive on
    ExpiresDefault "access plus 1 month"
    ExpiresByType text/css "access plus 1 day"
    ExpiresByType image/png "access plus 1 week"
    ExpiresByType image/gif "access plus 1 week"
    ExpiresByType image/jpeg "access plus 1 week"
</IfModule>
# Compression pour fichiers JS
<IfModule mod_gzip.c>
    mod_gzip_on       Yes
    mod_gzip_dechunk  Yes
    mod_gzip_minimum_file_size 512
    mod_gzip_maximum_file_size 1000000
    mod_gzip_item_include file \.js$
    mod_gzip_item_include mime ^application/x-javascript.*
</IfModule>

# Cache
<IfModule mod_expires.c>
    ExpiresActive on
    ExpiresByType application/x-javascript "access plus 2 month"
    ExpiresByType application/javascript "access plus 2 month"
    ExpiresByType text/javascript "access plus 2 month"
</IfModule>

Solution alternative en PHP

La fonction ob_gzhandler et l'ensemble des fonctions de type ob_* disponibles depuis PHP4 permettent la gestion du tampon de sortie, c'est à dire des données qui seront envoyées au navigateur. Il est alors possible de générer le contenu complet de la page et de le compresser avec Gzip avant envoi. On active le tampon en début de script avec ob_start, et on le vide à la fin avec ob_end_flush.

La fonction ob_gzhandler a le mérite de vérifier les types de compressions supportés par le navigateur (gzip, deflate ou aucun) avant de retourner le contenu du tampon de manière appropriée. Si le navigateur ne supporte pas les pages compressées, cette fonction retournera false.

<?php
ob_start("ob_gzhandler");
?>
... Le reste du code ...
<?php
ob_end_flush();
?>

Bien sûr, ceci est à adapter en fonction de la structure de votre site. Il ne suffit pas toujours de placer ces instructions en début et en fin de script PHP car de nombreux CMS utilisent déjà leur propre système de buffer (tampon) interne.

Tests dans la pratique

Vous pourrez aisément vérifier le bon déroulement du transfert et de la décompression en vérifiant les propriétés de la page dans le navigateur (dans Firefox : clic bouton droit, Informations sur la page, onglet Général, Taille). Comparez la taille du fichier original et la taille lue (à l'octet près).

Taille de la page sous Firefox

Consultez également les indications fournies par les extensions de développement (type Firebug ou Outils de développement accessibles dans les navigateurs modernes). Celles-ci figurent dans l'onglet Réseau ou Network, et mentionnent les en-têtes HTTP de compression ainsi que la quantité de données téléchargées.

Réseau compression

Commentaires

Ce n'est pas nécessaire car c'est appelé automatiquement en fin de script (que le buffer soit activé avec ob_start ou via php.ini), mais dans la théorie la plus absolue, c'est plus propre, et cela permet de comprendre comment les choses se passent.

D'ailleurs pour ceux qui veulent encoder leurs fichiers JS, CSS, ... en utilisant la méthode PHP (que je déconseille si vous pouvez faire la méthode APACHE), vous pouvez passer par un script PHP qui se charge de vous renvoyer vos fichiers compressés.
Par exemple, un fichier gzip.php que vous utilisez en passant un nom de ressource en GET : gzip.php?f=style.css

On peut très bien imaginer une page ressemblant à ceci :
<?php
ob_start('ob_gzhandler');
$file = isset($_GET['f']) ? $_GET['f'] : null;
switch($file){
case 'style.css':
header("Content-type: text/css");
include('style.css');
break;
case 'ajax.js':
header("Content-type: application/x-javascript\");
include('ajax.js');
break;
}

Il suffira ensuite de remplacer vos appels aux fichiers JS et CSS classique par l'appel au script PHP suivie du nom de fichier en paramètres

Très bon article !

J'utilise pour ma part ces mode de compression sur plusieurs sites, le gain peut être très important, certains fichiers sont réduits au quart de leur taille initiale. Sur une page contenant beaucoup de texte, c'est particulièrement agréable.

Cela rend aussi l'utilisation de Jquery (58Ko non compressée, 18 Ko Gzippée ainsi) particulièrement légère en terme de bande passante, c'est toujours plaisant.

Merci dew : un bien beau résumé :)

Perso sur OVH (Apache 2) j'ai un .htaccess qui contient ça entre autres choses :
# 2 WEEKS
<FilesMatch "\.(jpg|jpeg|png|gif|css|js|swf)$">
Header set Cache-Control "max-age=1209600, public"
SetOutputFilter DEFLATE
</FilesMatch>
Cela me permet de faire d'une pierre deux coups mais je ne sais pas si c'est optimal...

Merci pour cet article d'autant plus que le sujet était quasiment inconnu pour certains d'entre nous, mis à part les solutions logicielles de compression d'HTML.

>> De nos jours, l'accent est souvent mis sur la performance des sites et les économies de bande passante.
Cela a toujours été le cas. Ca l'est particulièrement pour les mobiles aujourd'hui. Mais pour les postes fixes le besoin au niveau client est bien plus faible qu'hier au niveau HTML. Quand on fait le ratio HTML/objets multimédia (images, videos...), il s'avère auourd'hui que le HTML ne pèse souvent pas grand chose.

Un code propre est une étape préalable et bien plus importante que l'optimisation (elle y participe indirectement).

Et puis quand on parle d'optimisation, il est toujours bon de rappeller :
Règle 1: Ne pas optimiser.
Règle 2: Ne pas optimiser tant que nous n'avons pas de solutions parfaitement claire et non optimisée.
M. A. Jackson, Principles of Program Design

Mis à part ces remarques d'emmerdeur trolleur, je découvre tout cela avec beaucoup d'enthousiasme. Encore quelque chose à apprendre et mettre en œuvre. M'enfin cela n'a pas l'air sorcier, pas trop difficile à mettre en œuvre et particulièrement appréciable. Merci encore.

J'ai quand même une question. Si la prise en charge d'IE6 est importate pour un projet, peut-on quand même utiliser ces techniques ? (une remarque dans le tutoriel évoque des bugs possibles) Quels seraient les risques ?

@Heyoan : En ce qui concerne ton script, il n'est pas très conseillé. Effectivement, tu fait un règle qui définie la mise en cache pour des fichiers JPG, SWF, ... Ce qui est conseillé. Par contre, tu a inclut le DEFLATE dans cette règle hors il n'est pas conseillé d'activer DEFLATE sur des images ou des fichiers multimédia. Ces fichiers sont déjà compréssé.
Je te conseil de faire du DEFLATE ou GZIP uniquement sur des données de type texte comme un HTML, CSS, JS, ...
Ce n'est que mon avis, je n'ai jamais fais de test poussé la dessus mais attention !

Je plussoie arnolem : c'est complètement inutile de compresser des images censées déjà être compressées... et pire, ça bouffera du temps CPU pour un résultat nul.
Par contre, sur du fichier texte... voir une CSS passer de 8 ko à 2ko et une page html passer de 14 ko à 4 ko, c'est quand même intéressant !

Par contre, la mise en cache des images est souhaitable et même recommandée.

Je confirme également que le gain peut être très important, surtout pour tout ce qui est texte (HTML, JavaScript, CSS, XML me viennent à l'esprit). Non seulement on économise de la bande passante, mais un site ainsi comprimé peut apparaître plus "réactif".
Les problèmes de latence existeront toujours si vous avez beaucoup (trop) d'objet sur une page web. A ce propos, Google expérimente SPDY, un protocole qui pourrait remplacer HTTP, dont un des points est de toujours comprimer les données (http://dev.chromium.org/spdy/spdy-whitepaper). La boucle est bouclée.

Ironie de l'histoire, www.alsacreations.com n'utilise pas de compression HTTP (ce n'est pas une critique, juste une remarque)...

Oh, si si, la compression est utilisée par les CSS et JavaScript (combinée à une mise en cache) mais pas pour les pages HTML délivrées par le framework. C'est un choix délibéré. Voilà pourquoi je précise dans l'article qu'il ne s'agit que d'indications à appliquer avec prudence et à ajuster selon les cas de figure.

Tout ce qui concerne Apache dans l'article a été écrit avec Debian en tête, il serait bien de le préciser. En effet a2enmod ne fait pas parti du projet apache et /etc/apache2 n'est pas utilisé sur toutes les distributions (il existe notamment /etc/httpd).

Merci, une fois de plus pour cet n-ieme articles extrêmement intéressant.
J'en profite, pour remercier arnolem pour le petit bout de prog destiné à gziper le css et js quand on n'a pas la chance de pouvoir le faire coté apache. Ce qui est malheureusement mon cas pour la totalité de mes sites en exploitation.

Merci, une fois de plus pour cet n-ieme articles extrêmement intéressant.
J'en profite, pour remercier arnolem pour le petit bout de prog destiné à gziper le css et js quand on n'a pas la chance de pouvoir le faire coté apache. Ce qui est malheureusement mon cas pour la totalité de mes sites en exploitation.