Espace tutos dev’ & informatique 🐧

Apache : Servir automatiquement des images WEBP

Publié le
Article sur Apache server et le support des images au format webp

Introduction

Si vous avez déjà utilisé Google Page Speed Insights, alors vous avez sans doute rencontré le message « Diffusez des images aux formats nouvelle génération ». L’article d’aujourd’hui se concentre justement sur les images au format next-gen WEBP, et plus précisément comment les servir automatiquement aux navigateurs qui les supportent à la place des images standards JPEG et PNG. Le but est d’éviter d’avoir à modifier le code de toutes les pages du site pour pouvoir servir ces images quand c’est possible.

Concrètement, si un navigateur compatible demande une image https://example.com/image.png, il se verra servir en toute transparence (sans redirection) une image https://example.com/image.webp.

Note : le code a été testé sur un environnement Apache 2.4 chez l’hébergeur o2switch. N’oubliez pas de toujours faire une sauvegarde avant de modifier les fichiers de votre site !

Pré-requis

Pour pouvoir utiliser le code de cet article vous devez déjà disposer des images de votre site au format WEBP, mises en ligne dans le même dossier que le format normal, avec le même nom, mis à part l’extension. Par exemple, si vous avez une image appelée image.jpg dans un dossier /site.com/images, vous devrez avoir une image appelée image.webp dans ce même dossier. Bien que le code que nous allons écrire gère le cas du simple rajout de .webp à l’extension déjà existante (exemple : image.jpg.webp), garder l’ancienne extension n’est pas recommandé.

Optimisation : organisation de fichiers WEBP pour les servir automatiquement avec Apache
Placer les images classiques et WEBP dans le même dossier

Voici une capture d’exemple basée sur le site Code Colibri.

Si vous n’avez pas l’image au format WEBP, il va falloir la générer, par exemple avec l’outil en ligne de commande cwebp (voir la page de téléchargement et la documentation de la commande cwebp), ou depuis un site internet. Afin de limiter la longueur de cet article, j’en dirai plus dans une prochaine publication.

 

Optimisation : service automatique des images WEBP

Ajouter le support des fichiers .webp à Apache

Si votre Apache est un peu âgé alors il peut avoir besoin d’un rafraichissement… Les hébergeurs web ont parfois des versions d’Apache qui ne connaissent pas le format WEBP par défaut. Cela se traduit par un entête Content-Type avec un type MIME inattendu (en général application/octet-stream) qui déclenche le téléchargement du fichier au lieu de son affichage en tant qu’image. Pour tester c’est facile, tentez d’accéder avec votre navigateur compatible à une image WEBP, si un téléchargement démarre ou si l’entête Content-Type: image/webp n’est pas présent alors il faut indiquer à Apache ce qu’est une image WEBP.

Tout ce que nous avons à faire est de faire correspondre le bon type MIME (image/webp) à l’extension de fichier .webp, cela correspond au code ci-dessous (à insérer dans votre fichier htaccess) :

<IfModule mod_mime.c>

  # Mapping extensions et types mime
  AddType image/webp .webp

</IfModule>

A noter que si votre serveur gère déjà le format WEBP ce code est optionnel, mais ne causera pas de problème si vous l’insérez tout de même.

Réécrire les requêtes vers les images WEBP si supporté par le navigateur

Retourner de façon transparente le contenu WEBP au lieu du contenu classique se fait par le biais d’une réécriture interne – seul le serveur web en a connaissance, contrairement à une redirection où le navigateur est également au courant. Nous devons trouver le fichier WEBP de remplacement s’il existe, et modifier la requête en interne pour qu’elle pointe dessus si le navigateur déclare supporter le format WEBP.

Tout d’abord, voici le code qui fait cela. Je le décrirai juste après :

<IfModule mod_rewrite.c>
  RewriteEngine On

  # Trouver le fichier webp
  # 1) fichier.jpg -> fichier.jpg.webp
  RewriteCond %{REQUEST_URI} ^(.+)\.(?:jpe?g|png)$
  RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.webp -f
  RewriteRule ^(.+)$ "-" [NC,E=replacewebp:$1.webp]
  # 2) fichier.jpg -> fichier.webp
  RewriteCond %{REQUEST_URI} ^(.+)\.(?:jpe?g|png)$
  RewriteCond %{DOCUMENT_ROOT}/%1.webp -f
  RewriteRule (.+)\.(?:jpe?g|png)$ "-" [NC,E=replacewebp:$1.webp]

  # Réécriture vers le fichier webp s'il existe et est supporté
  RewriteCond %{ENV:replacewebp} !^$
  RewriteCond %{HTTP_ACCEPT} image/webp
  RewriteCond %{QUERY_STRING} !nooptim
  RewriteRule ^(.+)$ %{ENV:replacewebp} [NC,T=image/webp,L]

</IfModule>

Dans un premier temps on cherche un fichier WEBP qui existe, soit <la requete complete>.webp (exemple : /dossier/image.jpg.webp), soit <la requete sans extension>.webp (exemple : /dossier/image.webp). Si un fichier est trouvé, son chemin est stocké dans une variable d’environnement créée pour l’occasion, que nous appelons replacewebp. L’utilité de cette variable est double :

  • Si elle existe (non-vide) cela signifie qu’il existe un équivalent WEBP à l’image actuellement demandée
  • Elle contient le chemin de l’équivalent WEBP, que nous pouvons utiliser si le navigateur déclare le supporter

Dans un deuxième temps on réécrit la requête en interne vers l’équivalent WEBP (la valeur de replacewebp) si les conditions suivantes sont réunies :

  • L’équivalent WEBP existe bel et bien (la variable replacewebp n’est pas vide)
  • Le navigateur a envoyé un entête Accept contenant image/webp, indiquant qu’il est compatible
  • La requête d’image ne contient pas nooptim dans sa query string (exemple : image.png?nooptim=1)

On garde donc la possibilité de servir le format standard même si le navigateur supporte le format WEBP… C’est plus prudent !

Ajouter le header Vary: Accept pour les images avec alternative WEBP

Maintenant que pour une même URL le serveur peut retourner deux contenus différents, il est nécessaire d’informer le navigateur du visiteur et les éventuels intermédiaires de ce qui cause la variation du contenu servi. Cela se fait avec l’entête Vary, dont la syntaxe est Vary: Nom-du-header-variable. Ici ca sera Vary: Accept (car le contenu retourné varie selon la valeur de l’entête Accept envoyé par le navigateur).

Pourquoi est-ce important ? Vous savez sans doute que les navigateurs peuvent conserver un cache des ressources utilisées sur les sites visités. Lors d’une première requête le contenu utilisé est celui renvoyé par le serveur (il est alors mis en cache par le navigateur), puis lors des suivantes la version en cache est utilisée pour gagner du temps. Que se passe t’il si la première fois on était compatible WEBP mais que la seconde fois ce n’est pas le cas ? Cela dépend :

  • Sans entête Vary le navigateur va considérer que la ressource ne varie pas malgré l’absence de compatibilité WEBP la seconde fois, et va utiliser l’image WEBP en cache, provoquant une erreur d’affichage
  • Avec entête Vary le navigateur va considérer que la ressource varie peut-être (l’entête Accept étant différente), et va refaire une demande au serveur

S’il est improbable d’avoir un navigateur compatible WEBP qui subitement ne le soit plus, il est fréquent que le problème se pose avec les proxy-caches des entreprises. Les navigateurs compatibles feront récupérer le format WEBP au proxy, et sans l’entête Vary, ce dernier risque de reservir ce même contenu WEBP aux navigateurs incompatibles !

Le code suivant effectue cette opération de modification d’entête :

<IfModule mod_headers.c>

  # Ajouter le header Vary: Accept pour les images concernées
  # 1) Si une réécriture a eu lieu
  Header merge Vary Accept env=REDIRECT_replacewebp
  # 2) Si une réécriture était possible mais n'a pas eu lieu
  Header merge Vary Accept env=replacewebp

</IfModule>

Concrètement, on modifie l’entête si la variable d’environnement replacewebp contenant le chemin du fichier WEBP existe et n’est pas vide (= si un fichier .webp a été trouvé). Un point de détail intéressant est le préfixe REDIRECT_ ajouté au nom de la variable d’environnement. Les variables d’environnement se retrouvent préfixées ainsi après une réécriture.

Le code complet

Voici le code complet à mettre en place dans le fichier htaccess de votre site. Pensez à faire une sauvegarde avant, ça peut servir en cas de version Apache incompatible.

# BEGIN WebP support
<IfModule mod_mime.c>

  # Mapping extensions et types mime
  AddType image/webp .webp

</IfModule>
<IfModule mod_rewrite.c>
  RewriteEngine On

  # Trouver le fichier webp
  # 1) fichier.jpg -> fichier.jpg.webp
  RewriteCond %{REQUEST_URI} ^(.+)\.(?:jpe?g|png)$
  RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.webp -f
  RewriteRule ^(.+)$ "-" [NC,E=replacewebp:$1.webp]
  # 2) fichier.jpg -> fichier.webp
  RewriteCond %{REQUEST_URI} ^(.+)\.(?:jpe?g|png)$
  RewriteCond %{DOCUMENT_ROOT}/%1.webp -f
  RewriteRule (.+)\.(?:jpe?g|png)$ "-" [NC,E=replacewebp:$1.webp]

  # Réécriture vers le fichier webp s'il existe et est supporté
  RewriteCond %{ENV:replacewebp} !^$
  RewriteCond %{HTTP_ACCEPT} image/webp
  RewriteCond %{QUERY_STRING} !nooptim
  RewriteRule ^(.+)$ %{ENV:replacewebp} [NC,T=image/webp,L]

</IfModule>
<IfModule mod_headers.c>

  # Ajouter le header Vary: Accept pour les images concernées
  # 1) Si une réécriture a eu lieu
  Header merge Vary Accept env=REDIRECT_replacewebp
  # 2) Si une réécriture était possible mais n'a pas eu lieu
  Header merge Vary Accept env=replacewebp

</IfModule>
# END WebP support

Conclusion

Voila, nous servons automatiquement des images WEBP aux navigateurs compatibles si elles existent. Ce bout de code devrait sans doute rejoindre votre boite à outils, aussi bien pour vos futurs sites que vos travaux d’optimisation s’il y a lieu.

Bonne journée à vous !

Pour aller plus loin : le format AVIF

Les navigateurs récents supportent un autre format d’image – avec un meilleur taux de compression selon l’image – appelé le format AVIF, pour AV1 Image Format. A l’heure actuelle son support est encore peu répandu, mais vu les acteurs du web qu’il y a derrière (entre autres l’ensemble des GAFAM et Netflix), il devrait se développer assez rapidement.

Code Colibri déploie d’ores et déjà ce format sur ses sites et sur ceux de ses clients en plus du format WEBP, avec des configurations de serveur Apache servant au navigateur du visiteur le format qu’il supporte (original / WEBP / AVIF) en sélectionnant automatiquement la version la plus légère possible.

Ca vous intéresse ? Contactez-moi.

Références

CanIUse – Images WebP
Apache HTTPD – Documentation mod_headers
Apache HTTPD – Documentation mod_rewrite
Apache HTTPD – Documentation mod_mime
Google Developers – Conversion CWEBP

Partagez cet article  =>