Greboca  

Planète des utilisateurs Debian  -  Servir des images WebP & AVIF avec Nginx

 -  10 juin - 

WebP et AVIF sont deux formats d’image pour le web. Ils visent à produire des fichiers plus petits que les formats JPEG et PNG. Ils supportent tous deux la compression avec ou sans perte, ainsi que la transparence alpha. WebP a été développé par Google et est un dérivé du format vidéo VP81. Il est supporté par la plupart des navigateurs. AVIF utilise le format vidéo AV1, plus récent, pour obtenir de meilleurs résultats. Il est supporté par les navigateurs basés sur Chromium et a une prise en charge expérimentale pour Firefox2.

Votre navigateur sait décoder les formats WebP et AVIF. Votre navigateur ne sait décoder aucun de ces formats. Votre navigateur ne sait décoder que le format WebP. Votre navigateur ne sait décoder que le format AVIF.

Sans JavaScript, je ne peux pas déterminer les formats reconnus par votre navigateur.

Conversion et optimisation des images

Pour ce blog, j’utilise les commandes suivantes pour convertir et optimiser les images JPEG et PNG. Sautez à la section suivante si vous n’êtes intéressé que par la configuration de Nginx.

Images JPEG

Les images JPEG sont converties au format WebP avec cwebp.

find media/images -type f -name '*.jpg' -print0 \
  | xargs -0n1 -P$(nproc) -i \
      cwebp -q 84 -af '{}' -o '{}'.webp

Elles sont converties au format AVIF avec avifenc de libavif:

find media/images -type f -name '*.jpg' -print0 \
  | xargs -0n1 -P$(nproc) -i \
      avifenc --codec aom --yuv 420 --min 20 --max 25 '{}' '{}'.avif

Ensuite, elles sont optimisées avec jpegoptim compilé avec l’encodeur amélioré de Mozilla, via Nix. C’est une des raisons pour lesquelles j’aime Nix.

jpegoptim=$(nix-build --no-out-link \
      -E 'with (import {}); jpegoptim.override { libjpeg = mozjpeg; }')
find media/images -type f -name '*.jpg' -print0 \
  | sort -z
  | xargs -0n10 -P$(nproc) \
      ${jpegoptim}/bin/jpegoptim --max=84 --all-progressive --strip-all

Images PNG

Les images PNG sont sous-échantillonnées vers une palette RGBA 8 bits avec pngquant. Cette conversion réduit considérablement la taille des fichiers tout en étant pratiquement invisible.

find media/images -type f -name '*.png' -print0 \
  | sort -z
  | xargs -0n10 -P$(nproc) \
      pngquant --skip-if-larger --strip \
               --quiet --ext .png --force

Ensuite, elles sont converties au format WebP avec cwebp dans le mode sans perte.

find media/images -type f -name '*.png' -print0 \
  | xargs -0n1 -P$(nproc) -i \
      cwebp -z 8 '{}' -o '{}'.webp

Aucune conversion n’est effectuée vers AVIF : la compression sans perte n’est pas aussi efficace que pngquant et la compression avec perte n’est que marginalement meilleure par rapport à ce que j’obtiens avec WebP.

Garder uniquement les fichiers les plus petits

Je ne garde les images WebP et AVIF que si elles sont au moins 10 % plus petites que le format original : le décodage est généralement plus rapide pour les JPEG et PNG et les images JPEG peuvent être décodées progressivement3.

for f in media/images/**/*.{webp,avif}; do
  orig=$(stat --format %s ${f%.*})
  new=$(stat --format %s $f)
  (( orig*0.90 > new )) || rm $f
done

Je ne garde les images au format AVIF que lorsqu’elles sont plus petites que le format WebP.

for f in media/images/**/*.avif; do
  [[ -f ${f%.*}.webp ]] || continue
  orig=$(stat --format %s ${f%.*}.webp)
  new=$(stat --format %s $f)
  (( $orig > $new )) || rm $f
done

Nous pouvons comparer combien d’images sont conservées après conversion en WebP ou AVIF :

printf "     %10s %10s %10s\n" Original WebP AVIF
for format in png jpg; do
  printf " ${format:u} %10s %10s %10s\n" \
    $(find media/images -name "*.$format" | wc -l) \
    $(find media/images -name "*.$format.webp" | wc -l) \
    $(find media/images -name "*.$format.avif" | wc -l)
done

AVIF se comporte mieux que MozJPEG pour la plupart des fichiers JPEG alors que WebP ne fait mieux que pour un fichier sur deux :

       Original       WebP       AVIF
 PNG         64         47          0
 JPG         83         40         74

Informations complémentaires

Je n’ai pas détaillé mes choix pour les paramètres de qualité et il n’y a pas beaucoup de science là-dedans. Voici deux ressources qui donnent plus de détails sur AVIF :

Servir WebP & AVIF avec Nginx

Pour servir les images WebP et AVIF, il existe deux possibilités :

  • utiliser pour laisser le navigateur choisir le format qu’il prend en charge ;
  • utiliser la négociation de contenu pour laisser le serveur envoyer le format le mieux supporté.

J’utilise la deuxième approche. Elle repose sur l’inspection de l’en-tête HTTP Accept dans la requête. Pour Chrome, elle ressemble à ceci :

Accept: image/avif,image/webp,image/apng,image/*,*/*;q=0.8

Je configure Nginx pour qu’il serve l’image AVIF, puis l’image WebP et se rabatte sur l’image JPEG/PNG originale en fonction de ce que le navigateur annonce4 :

http {
  map $http_accept $webp_suffix {
    default        "";
    "~image/webp"  ".webp";
  }
  map $http_accept $avif_suffix {
    default        "";
    "~image/avif"  ".avif";
  }
}
server {
  # […]
  location ~ ^/images/.*\.(png|jpe?g)$ {
    add_header Vary Accept;
    try_files $uri$avif_suffix$webp_suffix $uri$avif_suffix $uri$webp_suffix $uri =404;
  }
}

Par exemple, supposons que le navigateur demande /images/ont-box-orange@2x.jpg. S’il prend en charge WebP mais pas AVIF, $webp_suffix est défini à .webp tandis que $avif_suffix est la chaîne vide. Le serveur essaie de servir le premier fichier existant dans cette liste :

  • /images/ont-box-orange@2x.jpg.webp
  • /images/ont-box-orange@2x.jpg
  • /images/ont-box-orange@2x.jpg.webp
  • /images/ont-box-orange@2x.jpg

Si le navigateur prend en charge les deux formats d’image, Nginx parcourt la liste suivante :

  • /images/ont-box-orange@2x.jpg.webp.avif (il n’existe jamais)
  • /images/ont-box-orange@2x.jpg.avif
  • /images/ont-box-orange@2x.jpg.webp
  • /images/ont-box-orange@2x.jpg

Eugene Lazutkin explique plus en détails comment cela fonctionne. Je n’ai présenté ici qu’une variation prenant en charge à la fois WebP et AVIF.


  1. VP8 est uniquement utilisé pour la compression avec perte. La compression sans perte utilise un autre algorithme↩︎

  2. L’implémentation dans Firefox était prévue pour Firefox 86 mais en raison du non-support des espaces de couleur, ce n’est toujours pas activé par défaut. ↩︎

  3. Le décodage progressif n’est pas prévu pour WebP mais pourrait être implémenté en utilisant des images de basse qualité pour AVIF. Voir cette question pour une discussion. ↩︎

  4. L’en-tête Vary permet de s’assurer qu’un cache intermédiaire (un proxy ou un CDN) vérifie l’en-tête Accept avant d’utiliser une réponse en cache. Internet Explorer a des difficultés avec cet en-tête et peut ne pas être en mesure de mettre la ressource en cache correctement. Il existe une solution de contournement, mais la part de marché d’Internet Explorer est désormais si faible qu’il est inutile de la mettre en œuvre. ↩︎

par Vincent Bernat

Planète des utilisateurs Debian

Planète des utilisateurs Debian - https://planet.debian.org/fr/

Debian et RMS, la montagne a accouché d’une souris

 -  18 avril - 

Suite au retour de Richard Stallman (RMS) au board de la Free Software Foundation (FSF), pas moins de neuf anciens Debian Project Leader (DPL) – (...)


Highvoltage 2.0 : Jonathan Carter réélu DPL pour 2021

 -  18 avril - 

Jonathan Carter (Highvoltage) vient d’être réélu Debian Project Leader (DPL). Il va donc pouvoir continuer le travail commencé l’année dernière. (...)


Carl Chenet: Les pièges en télétravail

 -  28 janvier - 

Rester connecté jusqu’à 22h, bosser en pyjama, suivre une réunion interminable depuis les toilettes… Toi aussi au bout de quelques semaines de (...)


Carl Chenet: Les relous du télétravail

 -  21 janvier - 

Tu les connais, ces gens qui rendent le télétravail pénible, tu bosses avec, certains de tes collègues ou des tes chefs. On va passer en revue dans (...)


David Mercereau: Sauvegarde de serveur écologiquement soutenable (à froid)

 -  Juin 2020 - 

Pour les besoins de mes activités pro et associatives, j’ai besoin de faire de la sauvegarde quotidienne de serveur. Les serveurs c’est déjà plutôt (...)