Comment optimiser la performance d'un site web côté serveur

Compression, gestion des délais d'expiration, du cache... Le serveur web peut subir un traitement de choc qui permettra d’améliorer grandement le temps de chargement des pages. Tour d'horizon.

Comme nous l’avons vu dans l’article Pourquoi optimiser un site web ?, améliorer la vitesse de chargement des pages d’un site à au moins quatre impacts directs : le nombre de visiteurs (et par effet de levier le référencement), un manque à gagner économique et un impact environnemental réel. Ensuite, nous avons évoqué de nombreux axes d’améliorations liés aux éléments graphiques et aux pages elles-mêmes dans les articles Optimiser l’utilisation des images d’une page web et la série sur l’optimisation de la structure et de l’organisation des pages.

Dans ce nouvel article, je vais aborder une autre facette d’un site web, le serveur de publication, qui lui aussi peut subir un traitement de choc en vue de l’amélioration du temps de chargement des pages.

 

Optimisation serveur, choix d’une approche

Aujourd'hui, plus de 60% des sites sont hébergés sur des serveurs Apaches (Usage statistics and market share of Apache for websites). Nous allons donc porter un focus spécifique sur ce serveur. Concernant Internet Information Server, deuxième serveur utilisé, à hauteur de 17%, je vous propose de vous appuyer en parallèle sur l’article de Steve Jacobson, Translate .htaccess Content to IIS web.config, qui vous permettra d’appliquer les ajustements nécessaires pour un fonctionnement dans les environnements Microsoft ou d’utiliser la solution proposée par HeliconTech.

Donc, le fichier .htaccess est un fichier de configuration permettant de définir le comportement d'Apache pour le répertoire dans lequel il est placé ainsi que ses sous-répertoires (avec IIS, il faudra utiliser le fichier web.config).  Il est donc tout à fait possible de mettre en place différentes stratégies d’accès et de gestion de contenu dans un même site en plaçant plusieurs fichiers .htaccess à différents endroits, le fichier parent restant actif tant que les directives qu'il contient ne sont pas modifiées. Pour notre approche de l'optimisation serveur, nous allons mentionner systématiquement le fichier .htaccess placé à la racine du site, car nous cherchons à améliorer le comportement pour l'ensemble du site.

Le fichier .htaccess est un simple fichier texte que vous pouvez éditer avec n'importe quel outil.

Note : Windows a du mal à travailler avec un fichier ne comportant qu'aucune extension. Je vous suggère donc d'utiliser un éditeur texte un peu évolué ou de renommer, le temps de l'édition, le fichier en htaccess.txt, par exemple.

.htaccess : attention !

Le fichier .htaccess prend effet immédiatement, car il est lu à chaque requête sur votre serveur. Cela signifie qu'une erreur de syntaxe ou de codage provoquera immédiatement un dysfonctionnement de votre site. Par ailleurs, ce fichier permet des réglages fins et très puissants, alors ne le manipulez pas à la légère !

Enfin, rappelons, à tout fin utile de debug, qu'une ligne débutant par # est considérée comme commentaire.

Vous pouvez faire des essais avec l'exemple ci-dessous (en prenant garde à ne pas écraser un fichier .htaccess déjà présent !), en commentant / décommentant les directives d'accès au contenu de votre site.

#
# Cette directive n'est pas prises en compte :
# interdiction d'accès absolue au contenu
# -------------------------------------------------------
# deny from all

#
# Alors que celle-ci est appliquée :
# autorisation de tous les accès
# -------------------------------------------------------
allow from all

Gérer le contenu

Comme évoqué plus haut, le fichier .htaccess est lu puis interprété lors de chaque accès au site. En conséquence, limitez son contenu (donc sa taille) au minimum possible :

  • réduisez les commentaires à leur partie utile,
  • supprimez toutes les directives inutiles, par exemple :
  1. des directives pour les fichiers *.swf n'ont pas de sens si vous n'utilisez pas Flash,
  2. des directives de restriction d'accès apportent-elles quelque chose pour un site totalement public ?

Pour exemple, le fichier .htaccess de 1 Ko ci-dessous :

#
# Controles de comportement du site : www.monsite.com
# - auteur : I. Ahounou
# - modification : 1er novembre 2012
# =======================================================
#
#
# Types MIME
# -------------------------------------------------------
AddType text/html .htm
AddType text/html .html
AddType application/x-httpd-php .foo
AddType text/javascript .js
AddType text/css .css
AddType application/x-shockwave-flash .swf
AddType video/x-flv .flv
AddType image/gif .gif
AddType image/jpg .jpg
AddType image/png .png
AddType application/pdf .pdf
AddType application/zip .zip
#
# Accès universel
# -------------------------------------------------------
allow from all

#
# Langue par défaut et charset
# -------------------------------------------------------
AddDefaultCharset UTF-8
AddLanguage fr-FR .html .htm .css .js
#
# -------------------------------------------------------
# Fin du fichier .htaccess
# -------------------------------------------------------
#
Nous remarquons de nombreuses choses inutiles :
  • la quantité de lignes de commentaires,
  • la description de types MIME standards,
  • l'accès universel au contenu, appliqué par défaut.

Il ne reste donc que l'application générique de la langue et du charset, qui sont une alternative viable si vous ne les décrivez pas dans le <header> des pages.

Il est alors préférable, et recommandé, de transformer ce fichier, par exemple comme suit.

#  I. Ahounou - 01/11/2012

# Langue par défaut et charset
AddDefaultCharset UTF-8
AddLanguage fr-FR .html .htm .css .js

Le fichier ne pèse plus que 128 octets et uniquement deux directives sont prises en compte, sans altérer le comportement du serveur, avec en prime une lisibilité accrue !

 

Vérifier votre serveur

Avant d’aller plus avant, il est nécessaire de vérifier que votre serveur dispose des modules nécessaires à une possible optimisation des flux. Pour cela, 3 options :

  1. vous disposez d’un accès aux paramètres et pouvez vérifier la liste des modules chargés,
  2. vous pouvez utiliser un appel à la fonction phpinfo(), si vous utilisez PHP,
  3. vous pouvez réaliser un test spécifique.

Dans le premier cas de figure, il suffit de cliquer / regarder pour obtenir l’information.

Dans le deuxième cas de figure, vous avez accès aux informations issues de PHP, ou exécutez le petit fichier phpinfo.php ci-dessous depuis votre site.

<?php
echo phpinfo();
?>

Recherchez alors l’une des informations suivantes :
  • HTTP_ACCEPT_ENCODING : deflate
  • HTTP_ACCEPT_ENCODING : gzip

Enfin, pour le troisième cas de figure, créez un premier fichier, index.html (de préférence dans un répertoire de test) :

<!--#printenv -->

Le deuxième fichier, .htaccess, contient :

SetEnv MOD_mod_deflate 0
SetEnv MOD_mod_gzip 0
SetEnv MOD_mod_headers 0
SetEnv MOD_mod_expires 0
<IfModule mod_deflate.c>
SetEnv MODULE_deflate 1
</IfModule>
<IfModule mod_gzip.c>
SetEnv MODULE_gzip 1
</IfModule>
<IfModule mod_headers.c>
SetEnv MODULE_headers 1
</IfModule>
<IfModule mod_expires.c>
SetEnv MODULE_expires 1
</IfModule>

Appelez le fichier index.html et recherchez les informations pour MODULE_xxx. Une valeur à 1 signifie que le module est disponible.

 

Utiliser la compression

Apache est capable de transmettre des fichiers compressés au navigateur client qui se charge alors de les décompresser. En activant cette possibilité, les fichiers transmis sont allégés de manière souvent drastique et transitent donc plus rapidement vers l'internaute.

Vous me direz que la contrepartie est du temps de décompression. Vous avez raison. Mais au regard de la puissance des machines actuelles, le gain de temps reste largement appréciable.

Les meilleurs résultats sont obtenus avec le module MOD_DEFLATE (à partir d’Apache v2.0) que nous allons employer, uniquement s'il est disponible, en réalisant un test avec la directive <IfModule module>...</IfModule>.

Note : suivant le serveur exécutant Apache, ce module est soit mod_deflate.c pour des environnements Unix, Linux, soit mod_deflate.so pour des environnements Windows. Le code ci-dessous propose les deux tests de présence en utilisant uniquement l'identificateur mod_deflate.


Pour être véritablement efficace, il est important de préciser aux différents proxy intermédiaires de la chaîne de transmission Internet de ne pas décompresser le contenu à la place de l'internaute. Ceci est possible en ajoutant une directive de type header append vary lors de l'utilisation de la compression serveur.

Mais bien entendu, il existe des incompatibilités avec certains navigateurs, comme Netscape et Internet Explorer (pour les anciennes versions). Nous pouvons traiter ces cas par des tests avec la directive BrowserMatch test.

<IfModule mod_deflate>
# Compression avec MOD_DEFLATE
AddOutputFilterByType DEFLATE text/html text/css text/plain text/xml text/javascript application/x-javascript application/x-httpd-php


#Pour les navigateurs incompatibles
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4.0[678] no-gzip
BrowserMatch bMSIE !no-gzip !gzip-only-text/html
BrowserMatch bMSI[E] !no-gzip !gzip-only-text/html

# Les proxies ne doivent pas décompresser à la place de l'internaute
Header append Vary User-Agent env=!dont-vary
</IfModule>

Si vous ne bénéficiez pas de MOD_DEFLATE, mais de MOD_GZIP, une autre structure est utilisable :

<IfModule mod_gzip.c>
mod_gzip_on Yes
mod_gzip_dechunk Yes
mod_gzip_add_header_count Yes
mod_gzip_send_vary Yes
mod_gzip_item_exclude reqheader "User-agent: Mozilla/4.0[678]"
mod_gzip_item_include file .(html?|xml|txt|css|js)$
mod_gzip_item_include handler ^cgi-script$
mod_gzip_item_include mime ^text/.*
mod_gzip_item_include mime ^application/x-javascript.*
mod_gzip_item_exclude mime ^image/.*
mod_gzip_item_exclude rspheader ^Content-Encoding:.*gzip.*
</IfModule>

Bien que la syntaxe soit différente, vous avez sans aucun problème compris l’application de mod_gzip_send_vary et l’exclusion du user-agent mozilla/4.

Enfin, si vous ne bénéficiez ni de MOD_DEFLATE, ni de MOD_GZIP, procédez rapidement à une installation ou tentez un contournement avec PHP en utilisant la fonction ob_gzhandler ou zlib.output (meilleur choix) et leurs différentes possibilités. La documentation est disponible, en français, sur php.net.

Pour vérifier de manière simple que vos fichiers sont compressés, vous pouvez utiliser le service proposé par un site comme WhatsMyIP.org.

Concernant Internet Information Server, il est reconnu que la compression n’est pleinement utilisable qu’à partir de la version 6, sous réserve d’un paramétrage spécifique que vous pouvez découvrir dans la documentation de Microsoft : Using HTTP Compression for Faster Downloads (IIS 6.0) ou sur Technos Sources.

 

Contrôler le cache du navigateur

En contrôlant l'activité du cache du navigateur client, il est possible de le forcer à enregistrer une copie locale des fichiers statiques (images, fichiers HTML, etc.), mais en conservant hors cache les fichiers dynamiques (PGP et CGI).

En réalisant cette opération, une partie non négligeable des fichiers ne transitent plus par Internet, mais sont directement lus depuis le disque dur de l'ordinateur. Gain de temps maximal pour l'affichage des pages, du moins pour les visiteurs réguliers !

Pour contrôler les fichiers mis en cache, nous allons utiliser la directive <FilesMatch test>...</FilesMatch>, pour manipuler le header.

Note : La syntaxe des tests utilisés par la directive FilesMatch s'appuie sur les expressions régulières. Vous pouvez vous familiariser, si nécessaire avec cette syntaxe, en utilisant les tutoriaux du site Expreg.com et réaliser des tests préliminaires depuis le site Annuaire-Info.com.

<IfModule mod_headers>
# Mise en cache pour un mois
<FilesMatch ".(ico|jpe?g|png|gif|swf|flv|gz)$">
Header set Cache-Control "max-age=2592000"
</FilesMatch>

# Mise en cache pour 2 heures
<filesMatch ".(css|js)$">
Header set Cache-Control "max-age=7200"
</filesMatch>

# Désactive la mise en cache
<FilesMatch ".(pl|php|cgi)$">
Header unset Cache-Control
</FilesMatch>
</IfModule>

 

Gérer le délai d'expiration

Dans la même logique que la gestion du cache, il est possible de préciser au serveur que des fichiers suffisamment récents sont déjà en possession de l'utilisateur et qu'il n'est pas utile de les transmettre une nouvelle.

Pour cela, il faut utiliser les directives Expires... du module mod_expires. Par exemple :
<IfModule mod_expires>
ExpiresActive On
ExpiresDefault "access plus 7200 seconds"
AddType image/x-icon .ico
ExpiresByType image/gif "access plus 2592000 seconds"
ExpiresByType image/ico "access plus 2592000 seconds"
ExpiresByType image/jpg "access plus 2592000 seconds"
ExpiresByType image/png "access plus 2592000 seconds"
ExpiresByType image/jpeg "access plus 2592000 seconds"
ExpiresByType image/icon "access plus 2592000 seconds"
ExpiresByType image/x-icon "access plus 2592000 seconds"
ExpiresByType text/css "access plus 2592000 seconds"
ExpiresByType text/html "access plus 7200 seconds"
ExpiresByType text/javascript "access plus 2592000 seconds"
ExpiresByType application/xhtml+xml "access plus 7200 seconds"
ExpiresByType application/x-javascript "access plus 2592000 seconds"
ExpiresByType application/x-shockwave-flash "access plus 2592000 seconds"
</IfModule>

Gestion des versions de fichier

Le ETAG permet d'identifier la version d'un fichier. Ainsi, en l'utilisant, le serveur sait s'il y a eu une modification du fichier depuis la précédente requête et peut donc décider, opportunément, de le transmettre. L'inconvénient de cette gestion, est que le serveur et le client doivent s'informer mutuellement pour chaque fichier, ce qui consomme de la bande passante et grève le délai de réactivité.

Ma recommandation est donc de désactiver cette fonctionnalité, en insérant les lignes suivantes :

Header unset ETag
FileETag none
 

Essais et conclusion

En appliquant les éléments évoqués ci-dessus, voici ce que j’ai pu mesurer avec mon site personnel.

                                              


Page d'accueil
                                     

       Page d'accueil         (images optimisées)       

Premier accès, cache vide    
   

Sans optimisation .htaccess

6,09 s 7,27 s

Avec optimisation .htaccess

3,95 s 3,71 s

Gains

2,14 s (= 35%) 3,56 s (= 49%)
     
Nouvel accès    

Sans optimisation

4,35 s 3,65 s

Avec optimisation

2,23 s 1,9 s
Gains 2,12 s (= 49%) 1,75 s (= 48%)
  Le constat est sans appel : en moyenne 45% de temps de chargement gagné pour chaque page.

Bien entendu, de nombreuses autres possibilités d’optimisation existent, notamment avec l’usage des Content Delivery Network, mais là, nous entrons dans le monde des experts et dépassons le cadre de cet article. De la même manière, si vous possédez votre propre infrastructure, différentes alternatives s’offrent à vous en termes de compression, routage et gestion de flux.

Quoiqu’il en soit, en matière d’optimisation, il faut toujours commencer à la source ! Maintenant, c'est à vous, car n'oublions pas qu'il est communément admis qu'à chaque seconde de chargement d'une page, c'est 10 internautes qui fuient !