mercredi 15 janvier 2014

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 s7,27 s
Avec optimisation.htaccess
3,95 s3,71 s
Gains
2,14 s (= 35%)3,56 s (= 49%)
   
Nouvel accès  
Sans optimisation
4,35 s3,65 s
Avec optimisation
2,23 s1,9 s
Gains2,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 !

PHP : faciliter la maintenance d'un site web avec la fonction include


En tirant parti de la fonction dynamique include, quelques astuces de conception permettent de rendre la maintenance d'un site web bien plus aisée. L'idée étant d'isoler les contenus récurrents dans des fichiers.
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 vu dans l’article Optimiser l’utilisation des images d’une page web comment améliorer la performance du poste le plus gourmand en ressources inutiles, les images, avec la prétention de réduire, parfois, de plus de 75% le volume global.
Pour poursuivre la série, je vous propose maintenant des astuces de conception avec lesquelles il sera bien plus aisé de maintenir votre site.

Astuces simples pour les fichiers

Dans ce chapitre, je vais évoquer l’optimisation de site Web, mais plutôt sous l’angle de la facilité de maintenance et d’évolution.
En effet, la plupart des pages adoptent une conception très similaire avec l’incontournable succession du <head> puis du <body>.
  • <head>
  1. l'ensemble des balises <meta>,
  2. un titre et une description,
  3. les appels aux fichiers CSS externes,
  4. les appels aux fichiers JavaScript externes,
  5. le code d'initialisation JavaScript
  • <body>
  1. l'en tête de la page,
  2. le breadcrumb (ou chemin de fer),
  3. le contenu, souvent organisé par colonnes et/ou blocs d'informations,
  4. le pied de page.


Si vous utilisez PHP, ce qui est le cas de 78,5% des sites[1], vous pouvez tirer un grand profit de la fonction include ! Pour rappel, cette fonction permet d’insérer, sans interprétation préalable, un fichier dans un autre. Si vous êtes prêt à transformer vos fichiers statiques HTML en fichiers dynamiques PHP, voyons comment nous pouvons très simplement améliorer la maintenance.
Note : Bien entendu, des fonctions équivalentes existent pour d’autres langages interprétés au niveau du serveur, alors consultez les documentations, l’adaptation ne devant certainement pas vous poser grand problème.
Pour le bloc <head>
A partir de la figure précédente, nous pouvons identifier des éléments communs à toutes les pages, au niveau des balises <meta>, des appels CSS et JavaScript ainsi que du code d’initialisation.
Tous ces éléments peuvent avantageusement être regroupés dans un fichierinclude/metaHeader.inc.php, appelé de manière générique et permettant ainsi de regrouper les constantes qui seront, de fait, plus faciles à maintenir sans erreur.

Pour le bloc <body>
Toujours à partir de la même figure de référence, il est possible d’identifier des éléments récurrents et statiques comme :
  • l’en-tête de page qui se déplace dans un fichier include/htmlHeader.inc.php,
  • la colonne de gauche, souvent associée à un menu, qui se retrouve dans le fichier include/htmlBodyLeft.inc.php,
  • la colonne de droite, souvent associée à de la publicité, un nuage de tags (etc.) qui s’exporte dans le fichier include/htmlBodyRight.php,
  • le pied de page, pour le copyright, les liens de contact (etc.) qui se place dans le fichier include/htmlFooter.inc.php.


En adoptant cette démarche, la structure de la page se simplifie :
<html> 
   <head>
        <?php include("include/metaHeader.inc.php"); ?>
        <!-- les éléments spécifiques de la page -->
        <title>...</title>
        <meta name="description" content="..." />
        <meta name="keywords" content="..." />
        <link href="css/maPage.css" rel="stylesheet" type="text/css" />
        <style>
              /* Ne contient que les éléments spécifiques */
              ...
        </style>
        <script src="scripts/..." type="text/javascript"></script>
        <script type="text/javascript">
              /* Ne contient que les éléments spécifiques */
              ...
        </script>
   </head>
   <body>
        <!-- en-tête -->
        <?php include("include/htmlHeader.inc.php"); ?>
        <!-- le spécifique -->
        ...
        <!-- breadcrumb -->
        ...

        <!-- colonne gauche -->
        <?php include("include/htmlBodyLeft.inc.php"); ?>
        <!-- le spécifique -->
        ...

        <!-- colonne centrale -->
        ...

        <!-- colonne droite -->
        <?php include("include/htmlBodyRight.inc.php"); ?>
        <!-- le spécifique -->
        ...
   </body>
</html>
Et, par exemple, pour le contenu du fichier include/metaHeader.inc.php, nous pouvons avoir :
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Language" content="fr">
<meta http-equiv="Expires" content="never" />
<meta http-equiv="Cache-Control" content="public" />
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE9" />
<meta name="distribution" content="global" />
<meta name="robots" content="Index,Follow" />
<meta name="country" content="France" />
<meta name="language" content="french" />
<meta name="copyright" content="..." />
<meta name="email" content="...@monsite.fr" />
<meta name="author" content="..." />
<meta name="designer" content="..." />
<meta name="publisher" content="..." />

<link rel="shortcut icon" href="shortcut.ico" type="image/x-icon" />
<link rel="icon" href="icon.png" type="image/png" />
<link rel="apple-touch-icon" href="apple-touch-icon.png" />
<link rel="alternate" type="application/rss+xml" title="..." href="..." />
<link href="css/default.css" rel="stylesheet" type="text/css" />
<link href="..." rel="stylesheet" type="text/css" />
<script src="scripts/default.min.js" type="text/javascript"></script>
<script src="..." type="text/javascript"></script>

<script language="javascript" type="text/javascript">
    /* code d'initialisation */
    ...
</script>
À ce stade, vous avez compris comment rationaliser votre structure de fichiers afin d'améliorer grandement la maintenance, sans qu'il soit nécessaire d'illustrer les fichiers include/html_xxx_.inc.php.
Avec cette approche, le fichier include/html_xxx_.inc.php étant appelé par une source PHP, vous pouvez aussi y associer des personnalisations contextuelles à partir d’informations obtenues depuis Apache.
L’exemple ci-dessous est un point de départ pour récupérer des informations de session, des informations concernant le navigateur de l’internaute, le script appelé ainsi que toute variable transmise. Ensuite, c’est du développement classique.
<?php

/*===================================================================
   SESSION
  (nécessite que ce script soit appelé en premier dans la page)
===================================================================*/
session_start();

// Transfo. des variables de session en variables PHP
if (isset($_SESSION)) {
   while (list($key, $value) = each($_SESSION))
         $$key = stripslashes($value);
} 

/*===================================================================
PARAMETRES
===================================================================*/

// Informations utilisateur / navigateur
define("_USER_AGENT",         $_SERVER['HTTP_USER_AGENT']);
define("_USER_LANGUAGE",      $_SERVER['HTTP_ACCEPT_LANGUAGE']);
define("_USER_ENCODING",      $_SERVER['HTTP_ACCEPT_ENCODING']);

// Informations page web appelée
define("_DOC_ROOT",           $_SERVER['DOCUMENT_ROOT']);
define("_DOC_FILENAME",       $_SERVER['SCRIPT_FILENAME']);
define("_DOC_QUERY_STRING",   $_SERVER['QUERY_STRING']);
define("_DOC_URI",            $_SERVER['REQUEST_URI']);
define("_DOC_SCRIPT",         $_SERVER['SCRIPT_NAME']);

/*===================================================================
VARIABLES TRANSMISES
===================================================================*/
// Pour assurer la compatibilité avec les versions inférieures à 4.1.0
if (!empty($_POST))  { $HTTP_POST_VARS = $_POST; }
if (!empty($_GET))   { $HTTP_GET_VARS = $_GET; }

// Transfo. des variables de l'URI et de formulaires en variables PHP
if (isset($HTTP_POST_VARS)) {
   while (list($key, $value) = each($HTTP_POST_VARS))
         $$key = stripslashes($value);
}
if (isset($HTTP_GET_VARS)) {
   while (list($key, $value) = each($HTTP_GET_VARS))
         $$key = stripslashes($value);
}

/*===================================================================
Code à partir d'ici
==v==v==v==v==v==v==v==v==v==v==v==v==v==v==v==v==v==v==v==v==v==v=*/
 
?>
La suite du travail de rationalisation ne devrait vous demander que peu d'effort pour être adapté à votre site.
Ce que nous venons d’évoquer est parfaitement applicable à des sites "simples", en prenant garde à ne pas faire d’amalgame entre la notion de simplicité et le nombre de pages. Dans le cas de sites bien plus complexes, cette approche n’est pas forcément pertinente et il sera bien plus efficace de s’appuyer sur des gestionnaires de templates[1] plus évolués. Dans cette logique, un premier pas peut-être réalisé avec des solutions comme WordPressJoomla ou Drupal. Vous pouvez vous faire une idée des CMS[2] les plus utilisés sur W3Techs.

Conclusion

Avec ce nouvel article, vous disposez de pistes sérieuses pour l’amélioration de la structure de votre site. Combinez cela avec tous les éléments déjà abordés et vous pourrez vous vanter d’avoir très largement contribué à l’optimisation de votre site web.
Impatient de connaitre la suite ? Alors rendez-vous très rapidement pour reprendre notre fil rouge de l’optimisation !
[3] Content Management System : gestionnaire de contenu

Performance web : optimiser son code HTML et CSS


De la compression du code HTML à la normalisation des CSS, voici quelques bonnes pratiques indispensables pour réduire le poids des pages web, et améliorer leur vitesse de chargement et d'exécution par les navigateurs.
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 vu dans l’article Optimiser l’utilisation des images d’une page web comment améliorer la performance du poste le plus gourmand en ressources inutiles, les images, avec la prétention de réduire, parfois, de plus de 75% le volume global.
Pour le dernier article de cette série, je vous propose quelques techniques concernant le code HTML des pages.

Améliorations HTML

Pour commencer cette partie, rappelons une trivialité : chaque balise HTML est prévue pour un usage.
Il n’est en effet pas rare de constater que certains développeurs préfèrent alourdir leurs pages en déclarant des propriétés spécifiques alors qu’un certain nombre existent déjà. Prenons l’exemple des titres qui sont normalement définis par des balises H1, H2, H3, etc. Il est pourtant courant de trouver des définitions CSS du type titre1, titre2, titre3 appliquées à des paragraphes. Ces manières de procéder sont à proscrire car, en plus de provoquer des descriptions supplémentaires (et donc alourdir le poids des pages), elles pénalisent dans le même temps votre référencement naturel.
La meilleure pratique est plutôt de trouver les éléments communs à tous les navigateurs et de ne décrire ensuite, pour les balises ad-hoc, que les propriétés spécifiques. Pour définir les éléments communs aux feuilles de style des navigateurs, vous pouvez, par exemple, consulter la page Default CSS Rules of HTML Elements in Various Browsers.
Pour s’affranchir des écarts de définition par défaut des navigateurs, certains concepteurs appliquent systématiquement un reset CSS. Cette façon de procéder est tout à fait justifiable pour avoir la parfaite maîtrise de mises en forme complexes, mais est-elle aussi pertinente pour des designs simples de pages ? Une évaluation de la surcharge CSS ne prend pas nécessairement beaucoup de temps et peut révéler une source d’optimisation potentielle.
Commun à IE 6 et 7   Commun à IE 8 et 9
h1 {
  display: block;
  font-size: 24pt;
  font-weight: bold;
  margin: 14pt 0;
}

h2 {
  display: block;
  font-size: 18pt;
  font-weight: bold;
  margin: 14pt 0;
}

h3 {
  display: block;
  font-size: 13.55pt;
  font-weight: bold;
  margin: 14pt 0;
}
h1 { 
  display: block;
  font-size: 2em;
  font-weight: bold;
  margin: 0.67em 0;
  page-break-after: avoid;
}

h2 {
  display: block;
  font-size: 1.5em;
  font-weight: bold;
  margin: 0.83em 0;
  page-break-after: avoid;
}

h3 {
  display: block;
  font-size: 1.17em;
  font-weight: bold;
  margin: 1em 0;
  page-break-after: avoid;
}
Pour une page utilisant une taille de police par défaut de 12 pt, les rendus par défaut des titres à l’écran peuvent être considérés comme similaires. Les écarts pour des tailles de 11 à 13 pt sont très certainement tout aussi acceptables.

Les différences ne concernent que les marges supérieures et inférieures, la gestion des sauts de page lors des impressions ainsi que d’éventuels changements du zoom, mais nous sommes, pour ces derniers éléments, dans une gestion évoluée des pages.
Firefox, Firefox 2 et 3    Opera 10.51               Chrome et Safari
h1 {
  display: block;
  font-size: 2em;
  font-weight: bold;
  margin: 0.67em 0;
}
h2 {
  display: block;
  font-size: 1.5em;
  font-weight: bold;
  margin: 0.83em 0;
}

h3 {
  display: block;
  font-size: 1.17em;
  font-weight: bold;
  margin: 1em 0;
} 
h1 { 
  display: block;
  font-size: 2em;
  font-weight: bold;
  margin: 0.67em 0;
}
h2 {
  display: block;
  font-size: 1.5em;
  font-weight: bold;
  margin: 0.83em 0;
}
h3 {
  display: block;
  font-size: 1.17em;
  font-weight: bold;
  margin: 1em 0;
}
h1 {
  display: block;
  font-size: 2em;
  font-weight: bold;
  margin: 0 0.67em 0;
}
h2 {
  display: block;
  font-size: 1.5em;
  font-weight: bold;
  margin: 0 0.83em 0;
}
h3 {
  display: block;
  font-size: 1.17em;
  font-weight: bold;
  margin: 0 1em 0;
}
Pour Firefox, Opera, Chrome et Safari, les définitions par défaut ne diffèrent que pour les marges des 2 derniers navigateurs et sont assimilables à celles d’Internet Explorer pour les versions 8 et 9. 
Note : les définitions spécifiques au Quirks mode déclarées par défaut pour Chrome et Safari ne sont pas prises en considération.

Si votre site utilise des affichages évolués, ou que vous souhaitez une maîtrise "absolue" de l’affichage, la normalisation CSS semble alors plus appropriée que les techniques de reset CSS. Vous pouvez par exemple utiliser normalize.css qui pallie, de manière optimisée, les écarts entre navigateurs.
Un autre aspect apparemment anodin concerne la page HTML codée, dans laquelle un développeur sérieux va placer des indentations, retours de lignes et commentaires. Par exemple, une indentation peut-être réalisée avec une tabulation (1 octet) ou 4 espaces consécutifs (4 octets). L’écart unitaire de 3 octets peut sembler insignifiant, mais qu’en est-il sur une page de plusieurs centaines de lignes ? Ajoutons à cela les espaces laissés en fin de ligne, la surabondance de sauts de lignes utilisés pour aérer le code et les nombreux commentaires… Nous pouvons ainsi facilement récupérer des centaines, voire plusieurs kilos octets sur une page, sans aucune conséquence négative pour l’internaute ni perte de la lisibilité nécessaire à la maintenance.
Pour réaliser cette optimisation, il me parait utile, comme pour les fichiers JavaScript et CSS de maintenir deux versions, l’une dédiée au développement, l’autre rationalisée pour la production avec des outils comme HTML Compressor,Text Fixer ou Absolute HTML Compressor si vous préférez un outil installé sur votre poste de travail.
Avec ces nouveaux éléments dévoilés, vous continuerez à améliorer la vitesse de chargement de vos pages, tout en tenant compte d’impératifs souvent contraires entre les environnements de production et les besoins de maintenance.
Votre plan d’actions peut, sans difficulté, être construire en utilisant les têtes de chapitre comme points de repères fiables.
Les gains obtenus seront, en proportion, fort probablement inférieurs à ceux liés à l’optimisation des images (traité dans un précédent article), mais n’oublions pas notre leitmotiv : à chaque seconde de chargement supplémentaire, c’est 10 internautes qui fuient !
Dans un article à suivre (très rapidement), j’évoquerai un nouvel aspect de l’optimisation du temps de chargement des pages d’un site web, sous l’angle des serveurs qui délivrent le contenu.

Conclusion

Avec l’ensemble des articles publiés, vous détenez maintenant les clés essentielles de l’optimisation du poids des pages de votre site web.  En appliquant et en adaptant de manière pertinente toutes les pistes évoquées, vous devriez être surpris voir étonné des gains accumulés.
Tout est fini, nous pouvons nous dire au revoir ? Pas tout à fait. Un dernier aspect reste à évoquer ! Alors, à très bientôt pour découvrir ce qu’il est encore possible de faire en manipulant quelques paramètres du côté du serveur web.

Comment réduire le poids du code JavaScript et CSS


Pour réduire le poids des codes JavaScript et CSS, la meilleure démarche est d’utiliser des minimiseurs. Autres leviers : vectoriser les descriptions de style, et supprimez les descriptions non utilisées.
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 vu dans l’article Optimiser l’utilisation des images d’une page web comment améliorer la performance du poste le plus gourmand en ressources inutiles, les images, avec la prétention de réduire, parfois, de plus de 75% le volume global.
Dans cet article, nous revenons à l’objectif premier : réduire le poids global de votre site, avec un focus sur l’optimisation du poids des fichiers JavaScript et CSS.

Réduire le poids du JavaScript et du CSS

Pour réduire le poids du JavaScript et du CSS, la meilleure démarche est d’utiliser des minimiseurs.
Vous en trouverez sans difficulté sur Internet, comme Closure Compiler de Google ou JSLint pour ce qui concerne le JavaScript, et Clean CSS ou CSS Compressor pour le CSS, sans oublier YUI Compressor, capable de traiter à la fois le JavaScript et le CSS.
Pour une utilisation autonome sur votre poste, je vous recommande l'excellentJASC (Just Another Script Compressor) qui fera des merveilles, à la fois avec vos fichiers JavaScript et CSS.
Voici ce qu'il est possible d'obtenir, par exemple avec des composants largement utilisés sur Internet :
   Non optimisé      Optimisé     Gain
JavaScript   
jQuery 1.8.3
  252 Ko
     32 Ko
     87% de gain
jQuery Mobile 1.2
  287 Ko
     111 Ko
     61% de gain
GMap3 5.0
  68 Ko
     27 Ko
     60% de gain
    
CSS   
jQuery Mobile Structure 1.2
  53 Ko
     47 Ko
     11% de gain
jQuery Mobile Theme 1.2
  52 ko
     26 Ko
     50% de gain

"J’utilise (ou mes équipes) toujours les versions compressées, évidemment !", me direz-vous. Parions un repas alors ? D’après Elie Bursztein, chercheur chez Google, 64% des sites[1] n’utilisent pas la version compressée de jQuery en production… Je crois que je viens de gagner deux repas gratuits par jour au restaurant !
De la même manière, la pertinence de jQuery n’est plus à démontrer, son usage étant devenu aujourd’hui pratiquement un pré-requis (utilisé par 89,9% des sites[2]). Mais avez-vous réellement analysé les fonctions qui vous sont nécessaires ? Par exemple, Zepto.js ou im.js sont des équivalents de jQuery, adoptant la même syntaxe et proposant uniquement les fonctions essentielles, en environ 8Ko au lieu de 32Ko, pour les versions compressées bien entendu. Un gain supplémentaire d’environ 75% sur le framework appelé par toutes vos pages mérite peut-être quelques essais !
Quoiqu’il en soit, la contrepartie, afin de conserver une réelle capacité de maintenance, est de travailler sur deux jeux de fichiers, un non minimisé, dans le cadre du développement et un optimisé en production.
Pour réduire le poids des fichiers CSS, une bonne pratique est aussi d’utiliser les syntaxes étendues.
Syntaxe classiqueSyntaxe étendue
#selector1 {
   margin-top: 50px;
   margin-right: 0;
   margin-bottom: 50px;
   margin-left: 0;
}

#selector2 {
  font-family: Helvetica;
  font-size: 14px;
  font-weight: bold;
  line-height: 1.5;
}

#selector3 {
  background-image:
    url(background.png);
  background-repeat: repeat-y;
  background-position: center top;
}
#selector1 {
   margin-top: 50px 0;
}

#selector2 {
   font: bold 14px/1.5 Helvetica;
}

#selector3 {
   background:
      url(background.png)
      repeat-y
      center top;
}
280 caractères
(espaces compris)
146 caractères (espaces compris),
soit une réduction de 48%

Pensez aussi à vectoriser vos descriptions de style. En plus de l’optimisation recherchée, vous y gagnerez en clarté et en lisibilité.
Syntaxe classiqueSyntaxe vectorisée
#selector1 {
  margin-top: 50px 0;
  font: bold 14px/1.5 Helvetica;
  background:
    url(background.png)
    repeat-y
    center top;
}
#selector2 {
  margin-top: 50px 0;
  font: bold 14px/1.5 Helvetica;
  background:
    url(background.png)
    repeat-y
    center top;
}

#selector3 {
  margin-top: 50px 0;
  font: bold 14px/1.5 Helvetica;
  background:
    url(background.png)
    repeat-y
    center top;
}
#selector1, selector2, #selector3 {
  margin-top: 50px 0;
  font: bold 14px/1.5 Helvetica;
  background:
    url(background.png)
    repeat-y
    center top;
}
360 caractères
(espaces compris)
143 caractères (espaces compris),
soit une réduction de 60%

Maintenant, une évidence toujours utile à rappeler : supprimez les descriptions non utilisées. Pour cela, réalisez des contrôles avec des outils comme Unused CSS(payant). Sinon, différents plugins pour navigateurs existent : Dust-Me Selectorsou CSS Usage pour Firefox ou l'outil d'audit intégré de Chrome.
Pour des constructions évoluées, il est possible de tirer profit d’outils/langages comme Less CSS qui apportent, non seulement des extensions syntaxiques majeures, mais aussi la possibilité de compiler le CSS pour produire une syntaxe optimisée.
Enfin, pour contenter les puristes, n’oubliez pas non plus de supprimer les unités de vos définitions nulles, car 0 reste 0, que ce soit en px, en em ou en %.
Concernant le JavaScript, les optimisations de performance que vous obtiendrez sont fondamentalement liées à vos techniques de codage, ce qui dépasse largement le cadre de cet article. Je vous propose néanmoins une piste pour débuter avec l'article JavaScript Optimization de Jeff Greenberg.

Conclusion

En utilisant les techniques et outils présentés dans cet article, une nouvelle étape majeure est franchie dans l’optimisation du poids de vos pages web et donc, de la dynamique de chargement des pages. 


Dans un ultime article à paraitre très prochainement, nous finirons ce tour d’horizon en évoquant des améliorations possibles sur votre code HTML.

Optimisation web : organiser le code JavaScript et CSS


Zoom sur les axes techniques d’optimisation liés à la structure et la conception des pages web, et la gestion des dépendances JavaScript et CSS. Un équilibre entre sur-optimisation et perception client est à trouver.
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 4 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 vu dans l’article Optimiser l’utilisation des images d’une page web comment améliorer la performance du poste le plus gourmand en ressources inutiles, les images, avec la prétention de réduire, parfois, de plus de 75% le volume global.
Dans cette nouvelle série d’articles, je vais évoquer avec vous des axes techniques d’optimisation en parlant de la structure et de la conception de la page ainsi que de la gestion des dépendances JavaScript et CSS.

L’organisation des pages

Le développement Web autorise beaucoup de latitudes, notamment dans l’organisation du code des pages.
Une bonne pratique consiste à regrouper le code JavaScript et CSS dans des fichiers externes qu’il est possible d’appeler par une syntaxe du type :
<link href="css/default.css" rel="stylesheet" type="text/css" />

<script src="scripts/default.js" type="text/javascript"></script>

Cette recommandation est aussi une manière d’améliorer véritablement votre référencement ainsi que la maintenabilité de votre site, alors évitez de la contourner !
Concernant le JavaScript, plusieurs grandes logiques d’organisation peuvent être considérées :
  1. Un fichier unique pour tout le site,
  2. Un fichier par fonction essentielle (par exemple, la gestion du menu principal, la gestion du nuage de tags, la gestion de la recherche, etc.), chaque fonction étant appelée par la page qui le nécessite,
  3. Un fichier par page,
  4. Un fichier générique pour toutes les fonctions communes au site et un fichier pour le spécifique de chaque page.


Avec le tableau précédent, nous comprenons aisément que le meilleur choix est de regrouper tous les éléments communs aux différentes pages du site, pour traiter les éléments spécifiques en parallèle.

L’appel des fichiers JavaScript et CSS externes

La tendance naturelle est de placer les appels aux fichiers CSS et JavaScript externes dans les balises <head> des pages.
Pourtant, nous savons que le nombre de requêtes de chargement simultanées est limité à deux. Une meilleure pratique est donc de laisser les appels CSS en début de page, mais de déplacer les appels JavaScript en fin de page.
De cette manière, nous favorisons le chargement du contenu de la page, ce qui augmente la perception de réactivité chez l’internaute, puisque les informations arrivent en premier. Ensuite seulement, les codes JavaScript sont chargés et appliqués. 
Un autre bénéfice est aussi identifiable lorsque le code provoque des manipulations d’images. Chargées en premier, ces dernières sont immédiatement disponibles, alors que dans le cas contraire l’exécution sera retardée, le temps que les éléments graphiques soient manipulables.
Le seul bémol à cette technique concerne les sites qui utilisent un grand nombre d’images ou sprites et qui proposeront alors, pendant quelques instants, un contenu disgracieux.
Un équilibre entre sur-optimisation et perception client est donc à trouver.

Conclusion

Nous venons de voir comment structurer au mieux une page ainsi que l’appel aux dépendances JavaScript et CSS afin d’améliorer la réactivité perçue par l’internaute. Dans un prochain article, j’évoquerai des astuces avec desquelles vous obtiendrez, sans aucun doute, un réel bénéfice de maintenance et de lisibilité.

Optimiser l’utilisation des images d’une page web : intégration HTML


Optimisation du nombre de requêtes, utilisation de sprites... Le traitement d'images peut être avantageusement complété par des pratiques pertinentes concernant l’intégration HTML.

Dans le précédent article, Optimiser l’utilisation des images d’un site web : traiter les images, nous avons évoqué cinq axes de travail pour un cure d’amaigrissement majeur du poids de votre site.
La valeur de ce travail peut être avantageusement complétée par des pratiques pertinentes concernant l’intégration HTML, que nous pouvons organiser autour de trois chapitres majeurs. Voyons cela ensemble.

Intégration HTML des images

Dans cette partie, nous n’allons pas chercher à optimiser le poids des images, mais plutôt à rendre le comportement du site plus véloce et augmenter sa réactivité.
Les bonnes pratiques nous recommandent de toujours spécifier les attributs heightet width, ainsi que, si nécessaire, hspace et vspace (à défaut d’une gestion CSS avecmargin et padding) pour les balises <IMG>.
Ici, l’objectif est, d’une part de faciliter le travail du navigateur en lui donnant le maximum d’instructions exploitables et, d’autre part, éviter les phénomènes dits de reflow qui provoquent un re-calcul de l’affichage de la page.
Moins de travail de déduction des caractéristiques d’affichage manquantes et moins de calcul du navigateur signifient un affichage plus rapide et une meilleure réactivité de la page durant son chargement ou son redimensionnement.

Déclaration inline

La majorité des navigateurs (malheureusement, exception faite d’Internet Explorer) est aussi capable de traduire une déclaration d’image codée en base 64 si elle est directement spécifiée dans le code HTML ou CSS.
Nous savons que, par défaut, les standards Internet limitent les accès à deux chargements simultanés, augmentant ainsi les temps de latence pour des pages constituées d’un grand nombre de toutes petites images. Avec une déclaration inline, vous supprimez les multiples requêtes au profit d’une seule.
Par exemple, pour une image de 20x20 pixels, se chargeant en 0,6 seconde, l’encodage en base 64 produit environ 1500 octets de données, soit un temps de chargement de 0,7 secondes pour 10 images. Si 10 images de ce type sont utilisées dans la page, nous observons 4 périodes de latence d’environ 0,6 secondes. Au final, avec une conception traditionnelle, nous pouvons observer jusqu’à 3 secondes de temps de chargement, pour seulement 0,7 avec une déclaration inline.


Pour réaliser l’encodage base 64, vous pouvez aussi utiliser un service en ligne, comme par exemple Motobit - Base 64 encoder-decoder ouhttp://www.greywyvern.com ou encore préférer l''utilisation d''un outil Windows sur votre poste, comme celui développé par John Dyer, Css Image Embedder.

Sprites

L’utilisation de sprites est aussi une excellente technique de réduction du nombre de requêtes de chargement. L’idée est de regrouper le maximum d’images en une seule pour ensuite utiliser les propriétés CSS afin d’en afficher qu’une partie, en fonction du besoin.
Par exemple :


Une fois ce regroupement réalisé, vous pouvez utiliser deux techniques, la plus commune (et plus simple) utilisant la propriété CSS background et son positionnement dans une balise DIV de dimension définie, la deuxième s’appuyant sur le clipping d’image.
Exemple de code pour la première technique :
01    <style>
02    #faceBook {

03       width:32px;    /* la taille de l'icone */

04       height:32px;

05       background:url(img/optimisation-image-5.png) 0px 0px no-repeat;
06    }

07    #linkedIn {
08       width:32px;

09       height:32px;
10       background:url(img/optimisation-image-5.png) -32px 0px no-repeat;

11    }
12    #Rss {

13       width:32px;
14       height:32px;

15       background:url(img/optimisation-image-5.png) -64px 0px no-repeat;
16    }

17    #viadeo {
18       width:32px;

19       height:32px;
20       background:url(img/optimisation-image-5.png) -96px 0px no-repeat;

21    }
22    </style>
 Exemple de code pour la deuxième technique :
01    <style>
02    .clipDiv {
03       position:relative;
04       overflow:hidden;
05       width:32px; /* la taille de l'icone */
06       height:32px;
07    }
08    .faceBook {
09       position:absolute;
10       top:0;
11       left:0;
12       clip:rect(0 32px 32px 0);
13    }
14    .linkedIn {
15       position:absolute;
16       top:0;
17       left:-32px;
18       clip:rect(0 64px 32px 32px);
19    }
20    .Rss {
21       position:absolute;
22       top:0;
23       left:-64px;
24       clip:rect(0 96px 32px 64px);
25    }
26    .viadeo {
27       position:absolute;
28       top:0;
29       left:-96px;
30       clip:rect(0 128px 32px 96px);
31    }
32    </style>
33
34    <body>
35        <div class="clipDiv">
36           <img class="faceBook" src="img/optimisation-image-5.png" />
37        </div>
38        <div class="clipDiv">
39           <img class="linkedIn" src="img/optimisation-image-5.png" />
40        </div>
41        <div class="clipDiv">
42           <img class="Rss" src="img/optimisation-image-5.png" />
43        </div>
44        <div class="clipDiv">
45           <img class="viadeo" src="img/optimisation-image-5.png" />
46        </div>
47    </body>
Pour aller plus loin concernant l’utilisation des sprites, vous pouvez consulter un premier tutorial AlsaCréations et CSS Sprites with Inline Images.  

Conclusion

Avec toutes les informations présentées, vous pouvez dès maintenant (avec plus ou moins d’huile de coude suivant que vous soyez décideur, chef de projet, amateur éclairé ou débutant) rendre votre site véritablement plus véloce et réactif en appliquant rigoureusement un plan d’actions établi sur six axes :
  1. supprimer les images inutiles au profit de propriétés CSS comme background, margin et padding,
  2. choisir un format normalisé (jpg, png ou gif) et le plus efficace possible,
  3. ajuster la taille de l’image en fonction de la dimension voulue dans le navigateur,
  4. utiliser une résolution de 72 dpi ou moins,
  5. compresser chaque image, jusqu’au point limite de détérioration visuelle,
  6. optimiser l’intégration HTML.

Il n’est pas rare de réduire le volume des éléments graphiques de plus de 75%, ce qui est, avouons-le, un gain plus que substantiel.
Et n’oubliez pas : à chaque seconde de chargement, c’est 10 internautes qui fuient !
Dans un article à suivre (très rapidement), j’évoquerai un autre aspect de l’optimisation du temps de chargement des pages d’un site web, sous un angle exclusivement technique en parlant de la structure et de la conception de la page ainsi que de la gestion des dépendances JavaScript et CSS.