La conception Web réactive est devenue la norme pour les sites Web et les applications Web modernes, offrant une expérience cohérente sur une grande variété d'appareils tout en optimisant l'affichage pour chaque appareil. Cependant, les appareils modernes varient non seulement en termes de taille d’écran, mais également de densité de pixels. La balise img
HTML5 fournit un certain nombre de fonctionnalités qui permettent au navigateur de sélectionner la ressource la plus appropriée si le serveur fournit plusieurs variantes. Si le site Web déploie plusieurs tailles différentes de la même image, le navigateur Web peut choisir la taille la mieux adaptée à son environnement actuel.
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 cet article de blog, nous montrons comment utiliser le module Image-Filter pour NGINX et NGINX Plus pour fournir des images réactives sans le casse-tête de la création et de la gestion d'une myriade de variantes d'images. Au lieu de cela, nous pouvons déployer une seule version « source » de chaque image que NGINX ou NGINX Plus redimensionne à la volée. Les informations contenues dans cet article s'appliquent à la fois à NGINX Open Source et à NGINX Plus (à l'exception des instructions distinctes dans Installation du module Image-Filter ) ; par souci de concision, nous faisons référence à NGINX Plus tout au long de cet article.
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.
Il est également possible de compiler le module Image-Filter à partir de la source et de le charger en tant que module compilé statiquement ou dynamique. Pour plus de détails, consultez le Guide d'administration NGINX Plus .
Avec le module Image-Filter, nous pouvons créer et déployer une seule version « source » de chaque image et demander à NGINX Plus de la redimensionner à la volée pour fournir toutes les variantes de taille demandées par le navigateur. Nous pouvons affiner les pages Web et les images réactives entièrement dans la source HTML, sans redimensionner et déployer manuellement les images sur notre 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).
serveur { écouter 80 ;
racine /var/www/public_html ;
emplacement ~ ^/img([0-9]+)(?:/(.*))?$ {
alias /var/www/source_images/$2 ;
image_filter_buffer 10 M ;
image_filter resize $1 - ;
}
}
Le bloc serveur
définit la manière dont NGINX Plus gère les requêtes HTTP entrantes. La directive listen
indique à NGINX Plus d’écouter sur le port 80, la valeur par défaut pour le trafic HTTP. La directive root
spécifie l'emplacement sur le disque de ce site Web. Dans cet exemple simple, nous utilisons un site Web statique hébergé par NGINX Plus, mais il est également courant que NGINX Plus agisse comme un proxy inverse pour le contenu dynamique ou un connecteur d'application tel que FastCGI. Tous ces cas d'utilisation peuvent tirer parti du module Image-Filter tel que décrit ici en déployant les images sources sur le serveur NGINX Plus.
Le bloc d'emplacement
utilise une expression régulière pour faire correspondre les demandes d'actifs stockés dans n'importe quel répertoire commençant par /img suivi d'un ou plusieurs chiffres. Les chiffres sont capturés comme variable $1
et le nom de fichier qui suit est capturé comme 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 n'est pas sous le chemin racine
, les clients ne peuvent donc pas demander directement les images sources.
Comme nos images sources sont susceptibles d’être très grandes, peut-être des milliers de pixels de large, 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 les fichiers image d'une taille maximale de 10 Mo.
Enfin, la directive image_filter
indique au module Image-Filter de redimensionner l’image source à la largeur capturée à partir du suffixe du nom du répertoire /img . Le tiret (‑) indique à NGINX Plus de conserver le rapport hauteur/largeur 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;
emplacement ~ ^/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 agit à la fois comme serveur Web frontal et comme serveur d'images réactif. Le traitement d'images peut être très gourmand en ressources de calcul, ce qui peut entraîner des charges de travail extrêmement élevées et rendre NGINX Plus sujet aux attaques DoS. Pour éviter la situation dans laquelle le serveur Web frontal ne peut pas accepter immédiatement de nouvelles demandes car tous les processus de travail sont occupés par des demandes de redimensionnement d’image, nous vous recommandons d’exécuter une instance NGINX Plus distincte dédiée au traitement d’image. Cela isole les processus de travail du serveur Web frontal de ceux qui effectuent le traitement des images. Pour exécuter une instance distincte de NGINX Plus sur le même hôte, spécifiez un fichier de configuration différent sur la ligne de commande :
$ sudo nginx -c /etc/nginx/resize-server.conf
La manière la plus efficace de voir des images réactives en action est d'observer un navigateur prendre des décisions sur la variante d'image srcset
à utiliser lorsque la taille de la fenêtre d'affichage change. Voici la source HTML d'une galerie d'images simple. Notez qu'à des fins de démonstration, les variantes de taille sont légèrement différentes pour chaque image, créant de nombreux « points d'arrêt » possibles auxquels le navigateur peut choisir une variante différente.
<!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."