Le design Web adaptatif est devenu la référence pour les sites et applications Web modernes, assurant une expérience uniforme sur une grande diversité d'appareils tout en optimisant l'affichage selon chaque appareil. Pourtant, les appareils actuels diffèrent non seulement par leur taille d’écran, mais aussi par leur densité de pixels. La balise HTML5 img
offre plusieurs fonctionnalités qui permettent au navigateur de choisir l'élément le plus adapté si le serveur propose plusieurs variantes. Quand votre site propose plusieurs tailles d'une même image, le navigateur sélectionne la taille la plus adaptée à son contexte.
Les images réactives peuvent donc permettre au navigateur Web de produire un rendu qui correspond étroitement à l’intention du concepteur. Cela améliore l'expérience utilisateur, mais cela représente également une charge supplémentaire pour les équipes de développement et d'exploitation, qui doivent désormais créer et déployer de nombreuses variantes d'images ainsi que l'image par défaut.
Dans ce billet, nous expliquons comment utiliser le module Image‑Filter pour NGINX et NGINX Plus afin de fournir des images adaptatives, sans vous compliquer la vie avec la création et la gestion de multiples variantes d’images : déployez simplement une version « source » de chaque image, que NGINX ou NGINX Plus redimensionne à la volée. Les informations présentées s’appliquent à NGINX Open Source et à NGINX Plus (sauf pour les instructions spécifiques dans Installation du module Image‑Filter) ; nous utilisons NGINX Plus pour simplifier nos explications.
srcset
L'outil principal pour fournir des images réactives est l'attribut srcset
de la balise img
HTML5. Nous pouvons l'utiliser pour spécifier un certain nombre de variantes d'éléments d'image pour différentes densités de pixels et tailles de fenêtre d'affichage. Viewport est un terme générique désignant l'espace d'affichage disponible pour le navigateur Web, qu'il s'agisse d'une fenêtre sur un ordinateur de bureau ou d'une application 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
et l'attribut srcset
nomme deux variantes : une pour les écrans avec une densité de pixels standard ( 1x
) et une seconde pour les écrans avec une double densité de pixels, 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 sophistiqué suivant définit un certain nombre de variantes d'éléments d'image à afficher, en fonction de la largeur de la fenêtre d'affichage. L'attribut sizes
spécifie que le navigateur restitue une image dans la moitié de la fenêtre d'affichage si celle-ci est plus large que 10em
et utilise la totalité de la fenêtre d'affichage dans le cas contraire. Le navigateur détermine la quantité d'espace disponible pour l'image et sélectionne la variante d'image qui correspond le mieux à l'espace disponible, en « arrondissant » généralement à la largeur supérieure (suffixe w
) et en redimensionnant en interne l'image pour remplir exactement l'espace.
< 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 réactives en spécifiant une gamme de variantes d’éléments d’image est facile à coder et très efficace. Cependant, cela présente des défis en termes de création et de gestion des variantes d’image elles-mêmes. Vous devez effectuer un grand nombre de redimensionnements d’images de pré-production et déployer un nombre beaucoup plus important de fichiers sur le serveur. Il peut être long d’affiner le nombre et la taille optimaux de chaque variante, ce qui rend ensuite difficile de tester l’accessibilité de chaque variante d’élément d’image.
Pour plus d'informations sur l'attribut srcset
et d'autres techniques pour les images réactives, consultez cet excellent article de blog .
La procédure d’obtention de ce module Image-Filter diffère pour NGINX Plus et NGINX.
Le module Image-Filter est disponible en tant que module dynamique gratuit pour les abonnés NGINX Plus.
Obtenez le module lui-même en l'installant à partir du référentiel NGINX Plus.
Pour les systèmes Ubuntu et Debian :
$ sudo apt-get install nginx-plus-module-image-filter
Pour les systèmes RedHat, CentOS et Oracle Linux :
$ sudo yum install nginx-plus-module-image-filter
Activez le module en incluant une directive load_module
dans le contexte de niveau supérieur (« main ») du fichier de configuration nginx.conf (c’est-à-dire pas dans les contextes http
ou stream
).
charger_module modules/ngx_http_image_filter_module.so;
Rechargez NGINX Plus pour charger le module Image-Filter dans l’instance en cours d’exécution.
$ sudo nginx -s recharger >
Le moyen le plus simple d’installer le module Image-Filter est de l’obtenir à partir du référentiel officiel NGINX. Suivez ces instructions pour configurer votre système afin d'accéder au référentiel officiel NGINX, puis installez-le avec le gestionnaire de packages 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 installé, suivez les étapes 2 et 3 de l’installation du module Image-Filter pour NGINX Plus pour configurer NGINX et recharger.
Vous pouvez aussi compiler le module Image-Filter depuis le code source et le charger comme module statique ou dynamique. Pour plus de détails, consultez le Guide d'administration NGINX Plus.
Avec le module Image‑Filter, vous créez et déployez une version « source » unique de chaque image, et NGINX Plus la redimensionne automatiquement pour fournir toutes les variantes de taille demandées par le navigateur. Vous ajustez les pages web et les images responsives directement dans le code HTML, sans redimensionner ni déployer manuellement les images sur votre serveur web.
Dans cet exemple de fichier HTML, nous définissons quatre variantes d'image pour des appareils avec différentes densités de pixels.
< html > < head > < title > Logo réactif title > head > < body > < h2 > Sélection du logo en fonction de la densité de pixels 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. Au lieu de cela, la configuration NGINX Plus suivante fait correspondre les demandes d'actifs préfixés par /img et les transforme en demandes de redimensionnement de l'image dans 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 serveur
définit comment NGINX Plus traite les requêtes HTTP entrantes. La directive listen
indique à NGINX Plus d’écouter sur le port 80, la norme pour le trafic HTTP. La directive root
définit l’emplacement sur le disque de ce site web. Dans cet exemple simple, nous utilisons un site statique hébergé par NGINX Plus, mais il est courant que NGINX Plus serve aussi de proxy inverse pour du contenu dynamique ou un connecteur d’application comme FastCGI. Chacun de ces cas peut exploiter le module Image‑Filter présenté ici, en déployant les images sources sur le serveur NGINX Plus.
Le bloc location
utilise une expression régulière pour cibler les requêtes visant des ressources stockées dans un répertoire commençant par /img suivi d’un ou plusieurs chiffres. Nous capturons ces chiffres sous la variable $1
, et le nom de fichier qui suit sous la variable $2
. Nous employons ensuite la directive alias
pour servir cette requête depuis le répertoire sur disque contenant nos images sources. Ce répertoire n’étant pas dans le chemin root
, les clients ne peuvent pas accéder directement aux images sources.
Nos images sources pouvant atteindre plusieurs milliers de pixels de large, nous veillons à ce que le module Image‑Filter réserve suffisamment de mémoire pour les charger et les redimensionner. Dans cet exemple, nous utilisons la directive image_filter_buffer
pour gérer des fichiers image jusqu’à 10 Mo.
Enfin, la directive image_filter
demande 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 préserver le ratio d’aspect de l’image source.
La configuration décrite dans Faire correspondre la taille de l'image à la densité de pixels peut fournir n'importe quelle variante de taille d'une image en fonction du nom du répertoire utilisé pour demander l'image. Cependant, pour un environnement de production, nous ne voulons pas attendre que le serveur Web redimensionne les images pour chaque requête. Cela n’est pas bon pour la latence globale et peut également ajouter une surcharge CPU importante.
La solution la plus efficace consiste à mettre en cache nos variantes d’image redimensionnées afin que les requêtes ultérieures pour chaque variante soient traitées à partir du cache, sans passer par le module Image-Filter. Nous pouvons y parvenir avec la configuration NGINX Plus en définissant un serveur virtuel distinct qui effectue le redimensionnement de l'image et les requêtes proxy vers celui-ci uniquement si la taille d'image demandée n'est pas déjà dans le cache. Nous appelons cela le serveur d’images réactif .
Nous devons également prendre en compte les implications en matière de sécurité liées à l’autorisation de requêtes arbitraires pour effectuer des opérations de redimensionnement d’images. La configuration de la mise en cache n'est d'aucune aide si un attaquant devait effectuer des requêtes rapides sur des variantes d'images uniques telles que /img1001/mylogo.png , /img1002/mylogo.png , /img1003/mylogo.png et ainsi de suite. 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 résoudre ce problème, nous appliquons une limite de débit au serveur d’images réactif, mais pas au serveur frontal contenant les variantes mises en cache. La configuration suivante étend la configuration de la correspondance de la taille de l’image à 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 en cache avec la directive proxy_cache_path
. Le paramètre keys_zone
définit une zone de mémoire partagée pour l'index de cache (appelée resized
) et alloue 1 Mo, ce qui est suffisant pour environ 8 000 images redimensionnées. Le paramètre max_size
définit le point auquel NGINX Plus commence à supprimer les images les moins récemment demandées du cache pour faire de la place aux nouveaux éléments mis en cache.
La directive d'emplacement
pour le serveur Web frontal (celui qui écoute sur le port 80) utilise la directive proxy_pass
pour envoyer des requêtes préfixées par /img au serveur d'images réactives hébergé en interne (127.0.0.1:9001). La directive proxy_cache
active la mise en cache pour cet emplacement en spécifiant le nom du cache ( redimensionné
) à utiliser pour stocker les réponses du serveur d'images réactif. 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é max_size
et qu'elles font partie des moins récemment demandées) et que toutes les réponses erronées du serveur d'images réactif 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 réactif lui-même, nous spécifions une limite de débit avec la directive limit_req_zone
. La directive n’impose pas elle-même de limite de débit – elle définit une limite de débit de deux requêtes par seconde qui est ensuite appliquée au serveur d’images réactif en incluant la directive limit_req
dans son bloc serveur
(voir le paragraphe suivant). En règle générale, une limite de débit est associée à un attribut de la demande, 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 à la plus petite valeur possible, 3 Ko, car notre clé a une cardinalité fixe de un.
Le bloc serveur
pour le serveur d'images réactif écoute sur le port 9001. Nous incluons les directives allow
et deny
pour spécifier que seul localhost (le serveur Web frontal) peut se connecter au serveur d'images réactif. Nous appliquons ensuite la limite de débit précédemment définie en incluant la directive limit_req
; le paramètre burst
autorise 10 requêtes simultanées avant d'appliquer la limite de débit. Une fois la limite de débit en vigueur, les demandes excédentaires sont retardées jusqu'à ce qu'elles puissent être traitées dans la limite.
Le bloc d'emplacement
est identique à celui de la correspondance de la taille de l'image à la densité de pixels, mais il est désormais exercé uniquement lorsque l'image demandée n'est 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 NGINX Plus joue à la fois le rôle de serveur web frontal et de serveur d’images réactif. Le traitement des images demande beaucoup de ressources informatiques, ce qui peut générer une charge de travail très élevée et exposer NGINX Plus aux attaques par déni de service. Pour éviter que le serveur web frontal ne puisse plus gérer de nouvelles requêtes parce que tous ses processus sont occupés à redimensionner des images, nous vous conseillons de faire fonctionner une instance NGINX Plus dédiée uniquement au traitement des images. Ainsi, vous séparez les processus du serveur web frontal de ceux affectés au traitement des images. Pour lancer une instance séparée de NGINX Plus sur le même hôte, indiquez un fichier de configuration différent dans la ligne de commande :
$ sudo nginx -c /etc/nginx/resize-server.conf
Le moyen le plus efficace pour voir les images responsives en action consiste à observer un navigateur décider quelle variante d’image srcset
utiliser au fur et à mesure que la taille de la fenêtre change. Voici le code HTML d’une galerie d’images simple. Pour cette démonstration, notez que les variantes de taille diffèrent légèrement d’une image à l’autre, ce qui crée de nombreux « points d’arrêt » où le navigateur peut choisir une autre variante.
<!DOCTYPE html>
<html>
<tête>
<titre>Galerie d'images réactives</titre>
</tête>
<corps>
<h2>Galerie d'images réactives</h2>
<image src="/img100/1-dominos.jpg" tailles="(min-largeur: 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" > corps > html >
Les captures d’écran ci-dessous montrent le contenu de cette page Web dans un navigateur Chrome avec l’ inspecteur ouvert sur l’onglet Réseau . La colonne Nom affiche le chemin de chaque variante d'image demandée au serveur, ce qui nous permet de voir la taille choisie sans avoir à vérifier les journaux sur le 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épertoire /img dans la colonne Nom se situent entre ces valeurs).
Lorsque nous élargissons la fenêtre du navigateur dans la figure 3, le navigateur choisit des images entre 440 et 540 pixels de large (les quatre dernières images répertoriées). La valeur de ces images dans la colonne Initiateur est Autre .
Avec NGINX Plus et le module Image-Filter, nous pouvons fournir la taille d'image optimale pour les conditions actuelles du navigateur. Et nous pouvons le faire sans redimensionnement d’image en pré-production, sans traitement par lots ni sans avoir à gérer des centaines de variantes d’images sur le disque. Encore une autre façon dont NGINX Plus vous aide à obtenir une distribution d’applications sans faille.
Essayez vous-même les images réactives avec NGINX Plus – démarrez votre essai gratuit de 30 jours dès aujourd'hui ou contactez-nous pour discuter de vos cas d'utilisation .
« Cet article de blog peut faire référence à des produits qui ne sont plus disponibles et/ou qui ne sont plus pris en charge. Pour obtenir les informations les plus récentes sur les produits et solutions F5 NGINX disponibles, explorez notre famille de produits NGINX . NGINX fait désormais partie de F5. Tous les liens NGINX.com précédents redirigeront vers un contenu NGINX similaire sur F5.com."