NGINX et NGINX Plus fournissent des images adaptatives sans les tracas

NGINX | 29 septembre 2016

Photo : Phil Roeder

Utiliser le module Image‑Filter et l’attribut srcset pour redimensionner les images à la volée

Le responsive web design est devenu la norme pour les sites et applications web modernes. Il garantit une expérience cohérente sur une grande variété d’appareils, tout en optimisant l’affichage pour chacun d’eux. Cependant, les appareils modernes diffèrent non seulement par la taille de leur écran, mais aussi par leur densité de pixels. La balise HTML5 img offre plusieurs fonctionnalités permettant au navigateur de sélectionner la ressource la plus appropriée lorsque le serveur en propose plusieurs variantes. Si le site web déploie une même image dans plusieurs tailles, le navigateur peut choisir celle qui convient le mieux à l’environnement actuel.

Les images adaptatives permettent ainsi au navigateur web de produire un rendu qui correspond étroitement à l’intention du concepteur. Cela améliore l’expérience utilisateur, mais impose également une charge supplémentaire aux équipes de développement et d’exploitation, qui doivent désormais créer et déployer de nombreuses variantes de chaque image, en plus de l’image par défaut.

Dans cet article de blog, nous expliquons comment utiliser le module Image‑Filter pour NGINX et NGINX Plus afin de fournir des images adaptatives sans avoir à créer et à gérer une multitude de variantes de ressources d’image. Nous pouvons ainsi déployer une seule version « source » de chaque image, que NGINX ou NGINX Plus redimensionne à la volée. Les informations de cet article s’appliquent à la fois à NGINX Open Source et à NGINX Plus (à l’exception des instructions distinctes de la section Installation du module Image‑Filter). Par souci de concision, nous utilisons le terme NGINX Plus dans tout l’article.

L’attribut « srcset »

Le principal outil permettant de fournir des images adaptatives est l’attribut « srcset » de la balise HTML5 « img ». Il permet de spécifier plusieurs variantes d’une image pour différentes densités de pixels et tailles de zone d’affichage. Le terme « zone d’affichage » désigne de manière générique l’espace d’affichage dont dispose le navigateur web, qu’il s’agisse d’une fenêtre sur un ordinateur de bureau ou d’une application en plein écran sur un appareil mobile.

Dans l’exemple suivant, l’attribut src définit une image par défaut pour les navigateurs qui ne prennent pas en charge l’attribut srcset attribute et l’attribut srcset indique deux variantes : l’une pour les écrans à densité de pixels standard (1x) et l’autre pour les écrans à densité de pixels double, tels que les écrans Apple Retina™ et certains moniteurs 4K (2x).

<img src="/images/mylogo-default.png"  srcset="/images/mylogo-density1.png 1x, /images/mylogo-density2.png 2x">

L’exemple plus élaboré qui suit définit plusieurs variantes de ressource d’image à afficher en fonction de la largeur de la fenêtre d’affichage. L’attribut « sizes » indique que le navigateur affiche une image sur la moitié de la fenêtre d’affichage si celle-ci est plus large que 10em, et qu’il utilise sinon toute la largeur de la fenêtre. Le navigateur détermine l’espace disponible pour l’image et sélectionne la variante de ressource d’image qui correspond le mieux à cet espace, généralement en « arrondissant à la valeur supérieure » jusqu’à la largeur suivante (suffixe w) et en redimensionnant l’image en interne afin qu’elle occupe exactement l’espace disponible.

<img src="/images/racecar-default.jpg"  sizes="(min-width: 10em) 50vw, 100vw"
  srcset="/images/racecar-100px.jpg 100w, /images/racecar-225px.jpg 225w, /images/racecar-450px.jpg 450w, /images/racecar-675px.jpg 675w">

Cette approche de diffusion d’images adaptatives, qui consiste à spécifier une gamme de variantes d’une même ressource image, est facile à coder et très efficace. Cependant, elle pose des difficultés en matière de création et de gestion des variantes elles-mêmes. Elle nécessite de redimensionner un grand nombre d’images en préproduction et de déployer beaucoup plus de fichiers sur le serveur. Déterminer précisément le nombre et la taille optimaux de chaque variante peut prendre du temps, ce qui complique ensuite la vérification de l’accessibilité de chacune d’elles.

Pour en savoir plus sur l’attribut « srcset » et sur d’autres techniques permettant de créer des images adaptatives, consultez cet excellent article de blog.

Installation du module Image‑Filter

La procédure d’obtention du module Image‑Filter diffère selon qu’il s’agit de NGINX Plus ou de NGINX.

Installation du module Image‑Filter pour NGINX Plus

Le module Image‑Filter est disponible gratuitement sous forme de module dynamique pour les abonnés à NGINX Plus.

  1. Obtenez le module en l’installant depuis le dépôt NGINX Plus. Pour les systèmes Ubuntu et Debian :
$ sudo apt-get install nginx-plus-module-image-filter
  1. Pour les systèmes RedHat, CentOS et Oracle Linux :
$ sudo yum install nginx-plus-module-image-filter
  1. Activez le module en ajoutant une directive load_module correspondante dans le contexte de niveau supérieur (« main ») du fichier de configuration nginx.conf (c’est-à-dire pas dans les contextes http ou stream).
load_module modules/ngx_http_image_filter_module.so;
  1. Rechargez NGINX Plus afin de charger le module Image‑Filter dans l’instance en cours d’exécution.
$ sudo nginx -s reload>

Installation du module de filtrage d’images pour NGINX Open Source

Le moyen le plus simple d’installer le module Image‑Filter consiste à le récupérer depuis le dépôt officiel de NGINX. Suivez ces instructions pour configurer votre système afin qu’il puisse accéder au dépôt officiel de NGINX, puis installez le module à l’aide du gestionnaire de paquets de votre système d’exploitation.

Pour les systèmes Ubuntu et Debian :

$ sudo apt-get install nginx-module-image-filter

Pour les systèmes Red Hat, CentOS et Oracle Linux :

$ sudo yum install nginx-module-image-filter

Une fois le module installé, suivez les étapes 2 et 3 de la section « Installation du module Image-Filter pour NGINX Plus » pour configurer et recharger NGINX.

Il est également possible de compiler le module Image‑Filter à partir du code source et de le charger en tant que module compilé statiquement ou module dynamique. Pour plus de détails, consultez le Guide d’administration de NGINX Plus.

Adapter la taille de l’image à la densité de pixels

Avec le module Image‑Filter, nous pouvons créer et déployer une seule version « source » de chaque image, puis laisser NGINX Plus la redimensionner à la volée afin de fournir toutes les variantes de taille demandées par le navigateur. Nous pouvons ajuster avec précision les pages web adaptatives et les images directement dans le code source HTML, sans avoir à redimensionner manuellement les images ni à les déployer sur notre serveur web.

Dans cet exemple de fichier HTML, nous définissons quatre variantes d’image pour les appareils présentant différentes densités de pixels.

<!DOCTYPE html><html>
<head>
<title>Responsive Logo</title>
</head>
<body>
 
<h2>Logo selection based on pixel density</h2>

<img src="/img400/mylogo.png"
  srcset="/img400/mylogo.png 1x, /img800/mylogo.png 2x, /img1200/mylogo.png 3x, /img1600/mylogo.png 4x">
 
</body>
</html>

Les répertoires /img400, /img800, /img1200 et /img1600 n’existent pas réellement. À la place, la configuration NGINX Plus suivante intercepte les requêtes portant sur des ressources dont le chemin commence par /img et les transforme en requêtes visant à redimensionner l’image indiquée par le nom de fichier d’origine (par exemple, mylogo.png dans le code HTML précédent).

server {    listen 80;
    root /var/www/public_html;
 
    location ~ ^/img([0-9]+)(?:/(.*))?$ {
        alias /var/www/source_images/$2;
        image_filter_buffer 10M;
        image_filter resize $1 -;
    }
}

Le bloc server définit la manière dont NGINX Plus traite les requêtes HTTP entrantes. La directive listen indique à NGINX Plus d’écouter sur le port 80, le port par défaut pour le trafic HTTP. La directive root spécifie l’emplacement de ce site Web sur le disque. Dans cet exemple simple, nous utilisons un site Web statique hébergé par NGINX Plus, mais NGINX Plus peut également servir de proxy inverse pour du contenu dynamique ou de connecteur d’application tel que FastCGI. Tous ces cas d’utilisation peuvent tirer parti du module Image‑Filter comme décrit ici, en déployant les images sources sur le serveur NGINX Plus.

Le bloc location utilise une expression régulière pour faire correspondre les requêtes portant sur des ressources stockées dans tout répertoire dont le nom commence par /img suivi d’un ou plusieurs chiffres. Les chiffres sont capturés dans la variable $1 et le nom de fichier qui suit est capturé dans la variable $2. Nous utilisons ensuite la directive alias pour traiter cette requête à partir du répertoire sur le disque qui contient nos images sources. Notez que ce répertoire ne se trouve pas sous le chemin root, de sorte que les clients ne peuvent pas demander directement les images sources.

Comme nos images sources sont susceptibles d’être très volumineuses, avec une largeur pouvant atteindre plusieurs milliers de pixels, nous devons nous assurer que le module Image‑Filter alloue suffisamment de mémoire pour les charger et les redimensionner. Dans cet exemple, nous utilisons la directive « image_filter_buffer » pour prendre en charge des fichiers image d’une taille maximale de 10 Mo.

Enfin, la directive « image_filter » indique au module Image‑Filter de redimensionner l’image source selon la largeur extraite du suffixe du nom du répertoire /img. Le tiret (‑) indique à NGINX Plus de conserver les proportions de l’image source.

Considérations relatives à l’utilisation en production

La configuration décrite dans Adaptation de la taille de l’image à la densité de pixels permet de fournir une image dans n’importe quelle taille, selon le nom du répertoire utilisé pour la demander. Toutefois, dans un environnement de production, il n’est pas souhaitable d’attendre que le serveur web redimensionne les images à chaque requête. Cela nuit à la latence globale et peut également entraîner une importante surcharge du processeur.

La solution la plus efficace consiste à mettre en cache les différentes versions redimensionnées de nos images afin que les requêtes ultérieures pour chacune d’elles soient traitées à partir du cache, sans passer par le module Image‑Filter. Pour ce faire, nous pouvons configurer NGINX Plus en définissant un serveur virtuel distinct chargé de redimensionner les images, vers lequel les requêtes ne sont transmises par proxy que si la taille d’image demandée ne se trouve pas déjà dans le cache. Nous appelons ce serveur le « serveur d’images adaptatives ».

Figure 1. Configuration de production d’un serveur d’images adaptatives avec mise en cache activée sur le serveur web frontal

Nous devons également prendre en compte les implications en matière de sécurité liées au fait d’autoriser des requêtes arbitraires à effectuer des opérations de redimensionnement d’images. La mise en cache ne protège pas contre un attaquant qui enverrait rapidement des requêtes visant des variantes uniques de ressources d’image, telles que /img1001/mylogo.png, /img1002/mylogo.png, /img1003/mylogo.png, etc. Même avec un volume de requêtes relativement faible, de telles attaques peuvent provoquer un déni de service (DoS) en raison d’une utilisation excessive du processeur. Pour y remédier, nous appliquons une limitation du débit au serveur d’images adaptatives, mais pas au serveur frontal qui contient les variantes mises en cache. La configuration suivante étend celle présentée dans Adaptation de la taille des images à la densité de pixels en appliquant la mise en cache et la limitation du débit au module Image‑Filter.

proxy_cache_path /var/www/imgcache levels=1 keys_zone=resized:1m max_size=256m;
server {
    listen 80;
    root /var/www/public_html;

    location ~ ^/img([0-9]+)(?:/(.*))?$ {
        proxy_pass        http://127.0.0.1:9001;
        proxy_cache       resized;
        proxy_cache_valid 180m;
    }
}

limit_req_zone "1" zone=2persec:32k rate=2r/s;

server {
    listen 9001;
    allow 127.0.0.1;
    deny all;
    limit_req zone=2persec burst=10;

    location ~ ^/img([0-9]+)(?:/(.*))?$ {
        alias /var/www/source_images/$2;
        image_filter_buffer 10M;
        image_filter resize $1 -;
    }
}

Nous commençons par définir l’emplacement de nos images mises en cache à l’aide de la directive « proxy_cache_path ». Le paramètre keys_zone définit une zone de mémoire partagée pour l’index du cache (appelée resized) et lui alloue 1 Mo, ce qui suffit pour environ 8 000 images redimensionnées. Le paramètre max_size définit le seuil à partir duquel NGINX Plus commence à supprimer du cache les images les moins récemment demandées afin de libérer de l’espace pour de nouveaux éléments.

La directive location du serveur web frontal (celui qui écoute sur le port 80) utilise la directive proxy_pass pour envoyer les requêtes dont le chemin commence par /img au serveur d’images adaptatives hébergé en interne (127.0.0.1:9001). La directive proxy_cache active la mise en cache pour cet emplacement en indiquant le nom du cache (resized) à utiliser pour stocker les réponses du serveur d’images adaptatives. La directive proxy_cache_valid garantit que les images redimensionnées sont conservées dans le cache pendant au moins 180 minutes (sauf si le cache a dépassé la valeur de max_size et qu’elles figurent parmi les images les moins récemment demandées) et que les réponses erronées du serveur d’images adaptatives ne sont pas mises en cache.

Pour une description détaillée de la mise en cache, consultez « Guide de la mise en cache avec NGINX et NGINX Plus ».

Avant de définir le serveur d’images adaptatives lui-même, nous spécifions une limite de débit à l’aide de la directive limit_req_zone. Cette directive n’applique pas elle-même de limite de débit : elle définit une limite de deux requêtes par seconde, qui est ensuite appliquée au serveur d’images adaptatives en incluant la directive limit_req dans son bloc server (voir le paragraphe suivant). En règle générale, une limite de débit est associée à un attribut de la requête, mais dans ce cas, nous spécifions la valeur de clé statique "1" afin que la limite s’applique à tous les demandeurs. Nous définissons la taille de la zone de mémoire partagée sur la plus petite valeur possible, soit 3 Ko, car notre clé a une cardinalité fixe de un.

Le bloc server du serveur d’images adaptatives écoute sur le port 9001. Nous incluons les directives allow et deny pour indiquer que seul localhost (le serveur web frontal) peut se connecter au serveur d’images adaptatives. Nous appliquons ensuite la limitation de débit définie précédemment en incluant la directive limit_req ; le paramètre burst autorise 10 requêtes simultanées avant l’application de la limitation de débit. Une fois celle-ci en vigueur, les requêtes excédentaires sont retardées jusqu’à ce qu’elles puissent être traitées dans la limite définie.

Le bloc location est identique à celui décrit dans « Adaptation de la taille de l’image à la densité de pixels », mais il n’est désormais utilisé que lorsque l’image demandée ne se trouve pas dans le cache et que la limite de débit n’a pas été dépassée.

Dans cette configuration de base, une seule instance de NGINX Plus fait office à la fois de serveur Web frontal et de serveur d’images adaptatives. Le traitement des images peut être très gourmand en ressources de calcul, ce qui risque d’entraîner des charges de travail extrêmement élevées et d’exposer NGINX Plus à des attaques par déni de service (DoS). Pour éviter que le serveur Web frontal ne puisse pas accepter immédiatement de nouvelles requêtes parce que tous les processus de travail sont occupés à traiter des demandes de redimensionnement d’images, nous vous recommandons d’exécuter une instance distincte de NGINX Plus dédiée au traitement des images. Cela permet d’isoler les processus de travail du serveur Web frontal de ceux qui traitent les images. Pour exécuter une instance distincte de NGINX Plus sur le même hôte, indiquez un autre fichier de configuration sur la ligne de commande :

$ sudo nginx -c /etc/nginx/resize-server.conf

Les images responsives en action

La meilleure façon de voir les images adaptatives en action consiste à observer comment un navigateur décide quelle variante d’image définie dans srcset utiliser lorsque la taille de la fenêtre d’affichage change. Voici le code source HTML d’une galerie d’images simple. Notez qu’à des fins de démonstration, les variantes de taille diffèrent légèrement pour chaque image, ce qui crée de nombreux « points de rupture » possibles auxquels le navigateur peut choisir une autre variante.

<!DOCTYPE html>
<html>
<head>
<title>Responsive Image Gallery</title>
</head>
<body>
 
<h2>Responsive Image Gallery</h2>

<img src="/img100/1-dominos.jpg" sizes="(min-width: 20em) 40vw, 100vw"
  srcset="/img110/1-dominos.jpg 110w, /img210/1-dominos.jpg 210w, /img310/1-dominos.jpg 310w, /img410/1-dominos.jpg 410w, /img510/1-dominos.jpg 510w, /img610/1-dominos.jpg 610w">

<img src="/img100/2-sign.jpg" sizes="(min-width: 20em) 40vw, 100vw"
  srcset="/img120/2-sign.jpg 120w, /img220/2-sign.jpg 220w, /img330/2-sign.jpg 330w, /img420/2-sign.jpg 420w, /img520/2-sign.jpg 520w, /img620/2-sign.jpg 620w">

<img src="/img100/3-thruppence.jpg" sizes="(min-width: 20em) 40vw, 100vw"
  srcset="/img130/3-thruppence.jpg 130w, /img230/3-thruppence.jpg 230w, /img330/3-thruppence.jpg 330w, /img440/3-thruppence.jpg 440w, /img550/3-thruppence.jpg 550w, /img660/3-thruppence.jpg 660w">

<img src="/img100/4-aces.jpg" sizes="(min-width: 20em) 40vw, 100vw"
  srcset="/img140/4-aces.jpg 140w, /img240/4-aces.jpg 240w, /img340/4-aces.jpg 340w, /img440/4-aces.jpg 440w, /img540/4-aces.jpg 540w, /img640/4-aces.jpg 640w">

</body>
</html>

Les captures d’écran ci-dessous montrent le contenu de cette page web dans un navigateur Chrome, avec l’onglet Inspecteur ouvert sur la section « Réseau ». La colonne « Nom » indique le chemin de chaque variante d’image demandée au serveur, ce qui permet de voir la taille choisie sans avoir à consulter les journaux du serveur web.

Avec la fenêtre d’affichage étroite de la figure 2, le navigateur a choisi des images d’une largeur comprise entre 220 et 310 pixels (les suffixes numériques des noms de répertoires /img dans la colonne Nom sont compris entre ces valeurs).

Figure 2. Galerie d’images adaptative lorsque la fenêtre d’affichage est étroite

Lorsque nous élargissons la fenêtre du navigateur dans la figure 3, le navigateur choisit des images d’une largeur comprise entre 440 et 540 pixels (les quatre dernières images répertoriées). Pour ces images, la valeur de la colonne Initiateur est Autre.

Figure 3. Galerie d’images adaptative lorsque la fenêtre d’affichage est plus large

Conclusion

Avec NGINX Plus et le module Image‑Filter, nous pouvons fournir une image dont la taille est optimale pour les conditions de navigation actuelles. Et ce, sans avoir à redimensionner les images en préproduction, à effectuer des traitements par lots ni à gérer sur disque des centaines de variantes de ressources d’image. Une autre façon pour NGINX Plus de vous aider à assurer une diffusion irréprochable de vos applications.

Testez vous-même les images adaptatives avec NGINX Plus : commencez votre essai gratuit de 30 jours dès aujourd’hui ou contactez-nous pour discuter de vos cas d’utilisation.


Partager
Mots-clés : F5 NGINX, Tech

À propos de l’auteur

Liam Crilly
Liam CrillyDirecteur senior de la gestion des produits

Autres articles de Liam Crilly

Articles de blog associés

Tendances actuelles des technologies cloud natives, de l’ingénierie de plateforme et de l’IA
NGINX | 13/10/2025

Tendances actuelles des technologies cloud natives, de l’ingénierie de plateforme et de l’IA

Découvrez les principaux enseignements tirés des réponses à l’enquête annuelle F5 NGINX sur les dernières tendances en matière de technologies cloud natives, de sécurité, d’ingénierie de plateforme et d’IA.

Quels contrôleurs Ingress NGINX sont affectés par les vulnérabilités CVE-2022-4886, CVE-2023-5043 et CVE-2023-5044 ?
NGINX | 03/11/2023

Quels contrôleurs Ingress NGINX sont affectés par les vulnérabilités CVE-2022-4886, CVE-2023-5043 et CVE-2023-5044 ?

Lorsque de nouvelles CVE sont signalées, il est facile de ne plus savoir si elles concernent un outil de contrôleur Ingress NGINX particulier, car il existe plusieurs projets. Cet article présente les CVE et explique comment distinguer les différents projets de contrôleurs Ingress afin de dissiper toute confusion.

Votre API est-elle en temps réel ? Testez sa latence avec l’outil rtapi de NGINX
NGINX | 11/03/2020

Votre API est-elle en temps réel ? Testez sa latence avec l’outil rtapi de NGINX

Votre API répond-elle aux requêtes en moins de 30 ms ? Si ce n’est pas le cas, il ne s’agit pas d’une API en temps réel offrant la faible latence exigée par la grande majorité des organisations. Mesurez la latence de votre API avec l’outil rtapi de NGINX et découvrez comment NGINX peut l’aider à offrir une réactivité en temps réel.

Un testeur d’expressions régulières pour NGINX et NGINX Plus
NGINX | 11/06/2019

Un testeur d’expressions régulières pour NGINX et NGINX Plus

Le testeur d’expressions régulières (regex) pour NGINX et NGINX Plus vous évite de tâtonner en vous indiquant si une expression régulière utilisée dans un bloc location ou map correspond aux valeurs comme prévu. Il génère la configuration appropriée et affiche les valeurs des groupes de capture.

Caches partagés avec les clusters de cache NGINX Plus, partie 1
NGINX | 24/01/2017

Caches partagés avec les clusters de cache NGINX Plus, partie 1

Optimisez la capacité de votre cache web en le répartissant entre plusieurs serveurs de cache web NGINX ou NGINX Plus. Découvrez comment procéder dans cet article.

Introduction aux microservices
NGINX | 19/05/2015

Introduction aux microservices

Les microservices suscitent actuellement beaucoup d’intérêt. Cet article de blog est le premier d’une série de sept consacrée à la conception, au développement et au déploiement de microservices.