« Si #nginx n’est pas placé devant votre serveur Node, vous vous y prenez probablement mal. » – Bryan Hughes sur Twitter
Node.js est l’outil de référence pour créer des applications serveur en JavaScript, le langage de programmation le plus populaire au monde. Offrant à la fois les fonctionnalités d’un serveur web et d’un serveur d’applications, Node.js est désormais considéré comme un outil essentiel pour tous les types de développement et de déploiement basés sur des microservices. (Téléchargez gratuitement le rapport Forrester sur Node.js et NGINX.)
Node.js peut remplacer ou compléter Java ou .NET pour le développement d’applications backend.
Node.js est monothread et utilise des E/S non bloquantes, ce qui lui permet de monter en charge et de prendre en charge des dizaines de milliers d’opérations simultanées. Il partage ces caractéristiques architecturales avec NGINX et résout le problème C10K – la prise en charge de plus de 10 000 connexions simultanées – pour lequel NGINX a également été conçu. Node.js est réputé pour ses hautes performances et la productivité qu’il offre aux développeurs.
Alors, qu’est-ce qui pourrait bien mal tourner ?
Node.js présente quelques points faibles et vulnérabilités susceptibles d’entraîner une baisse des performances, voire des pannes, dans les systèmes qui reposent sur cette technologie. Ces problèmes surviennent plus fréquemment lorsqu’une application web basée sur Node.js connaît une augmentation rapide du trafic.
Par ailleurs, Node.js est un excellent outil pour créer et exécuter la logique applicative qui génère le contenu principal et dynamique de votre page web. En revanche, il est moins adapté à la diffusion de contenu statique – comme des images et des fichiers JavaScript – ou à la répartition de charge entre plusieurs serveurs.
Pour tirer le meilleur parti de Node.js, vous devez mettre en cache le contenu statique, assurer le proxy et la répartition de charge entre plusieurs serveurs d’applications, et gérer les conflits de ports entre les clients, Node.js et les services auxiliaires, tels que les serveurs exécutant Socket.IO. NGINX peut remplir toutes ces fonctions, ce qui en fait un excellent outil pour optimiser les performances de Node.js.
Suivez ces conseils pour améliorer les performances des applications Node.js :
- Mettez en place un serveur proxy inverse
- Mettre en cache les fichiers statiques
- Répartir la charge du trafic entre plusieurs serveurs
- Relayer les connexions WebSocket
- Mettre en œuvre SSL/TLS et HTTP/2
Remarque : Pour améliorer rapidement les performances d’une application Node.js, modifiez votre configuration Node.js afin de tirer parti des serveurs multicœurs modernes. Consultez la documentation de Node.js pour savoir comment faire en sorte que Node.js génère des processus enfants distincts, en nombre égal à celui des processeurs de votre serveur web. Comme par magie, chaque processus est alors affecté à un seul et unique processeur, ce qui améliore considérablement les performances.
Conseil 1 – Mettez en place un serveur proxy inverse
Chez NGINX, Inc., nous sommes toujours quelque peu horrifiés lorsque nous voyons des serveurs d’applications directement exposés au trafic Internet entrant et utilisés au cœur de sites à hautes performances. C’est notamment le cas de nombreux sites basés sur WordPress, ainsi que de sites Node.js.
Node.js, plus que la plupart des serveurs d’applications, est conçu pour être évolutif, et son composant serveur web peut gérer assez efficacement un trafic Internet important. Mais servir des contenus web n’est pas la raison d’être de Node.js : ce n’est pas vraiment pour cela qu’il a été conçu.
Si votre site connaît un trafic élevé, la première étape pour améliorer les performances de l’application consiste à placer un serveur proxy inverse devant votre serveur Node.js. Cela protège le serveur Node.js d’une exposition directe au trafic Internet et vous offre une grande souplesse pour utiliser plusieurs serveurs d’applications, répartir la charge entre les serveurs et mettre le contenu en cache.

Placer NGINX devant une infrastructure de serveurs existante en tant que serveur proxy inverse, puis l’utiliser à d’autres fins, constitue l’un des principaux cas d’usage de NGINX, adopté par des dizaines de millions de sites web dans le monde entier.
L’utilisation de NGINX comme serveur proxy inverse pour Node.js présente des avantages spécifiques, notamment :
- Simplification de la gestion des privilèges et de l’attribution des ports
- Diffuser plus efficacement les images statiques (voir le conseil suivant)
- Gérer efficacement les plantages de Node.js
- Atténuer les attaques par déni de service (DoS)
Remarque: ces tutoriels expliquent comment utiliser NGINX comme serveur proxy inverse dans des environnements Ubuntu 14.04 ou CentOS, et offrent une vue d’ensemble utile à toute personne plaçant NGINX devant Node.js.
Astuce 2 – Mettre en cache les fichiers statiques
À mesure que la fréquentation d’un site basé sur Node.js augmente, le serveur commence à montrer des signes de saturation. À ce stade, deux mesures s’imposent :
- Tirer le meilleur parti du serveur Node.js
- Faciliter l’ajout de serveurs d’applications et la répartition de charge entre eux
C’est en fait facile à faire. Commencez par mettre en place NGINX en tant que serveur proxy inverse, comme décrit dans le conseil précédent. Cela facilite la mise en œuvre de la mise en cache, de l’équilibrage de charge (lorsque vous disposez de plusieurs serveurs Node.js), et bien plus encore.
Le site web de Modulus, une plateforme de conteneurs d’applications, propose un article utile sur l’optimisation des performances des applications Node.js avec NGINX. Lorsque Node.js effectuait seul tout le travail, le site de l’auteur pouvait traiter en moyenne près de 900 requêtes par seconde. Avec NGINX comme serveur proxy inverse chargé de diffuser le contenu statique, le même site a traité plus de 1 600 requêtes par seconde, soit des performances presque multipliées par deux.
Doubler les performances vous donne le temps de prendre des mesures supplémentaires pour accompagner la croissance future, par exemple en réexaminant (et éventuellement en améliorant) la conception de votre site, en optimisant le code de votre application et en déployant des serveurs d’applications supplémentaires.
Voici le code de configuration qui fonctionne pour un site web hébergé sur Modulus :
server { listen 80;
server_name static-test-47242.onmodulus.net;
root /mnt/app;
index index.html index.htm;
location /static/ {
try_files $uri $uri/ =404;
}
location /api/ {
proxy_pass http://node-test-45750.onmodulus.net;
}
}Cet article détaillé de Patrick Nommensen, de NGINX, Inc., explique comment il met en cache le contenu statique de son blog personnel, qui utilise la plateforme de blog open source Ghost, une application Node.js. Bien que certains détails soient propres à Ghost, une grande partie du code peut être réutilisée pour d’autres applications Node.js.
Par exemple, dans le bloc location de NGINX, vous souhaiterez probablement exclure certains contenus de la mise en cache. En règle générale, il est préférable de ne pas mettre en cache l’interface d’administration d’une plateforme de blog. Voici le code de configuration qui désactive la mise en cache de l’interface d’administration de Ghost (ou l’en exclut) :
location ~ ^/(?:ghost|signout) { proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://ghost_upstream;
add_header Cache-Control "no-cache, private, no-store,
must-revalidate, max-stale=0, post-check=0, pre-check=0";
}Pour obtenir des informations générales sur la diffusion de contenu statique, consultez le Guide d’administration de NGINX Plus. Le Guide d’administration comprend des instructions de configuration, plusieurs options permettant de répondre aux tentatives réussies ou infructueuses de recherche d’un fichier, ainsi que des méthodes d’optimisation pour obtenir des performances encore meilleures.
La mise en cache des fichiers statiques sur le serveur NGINX réduit considérablement la charge de travail du serveur d’applications Node.js, ce qui permet d’obtenir des performances nettement supérieures.
Conseil 3 – Mettez en place un répartiteur de charge Node.js
La véritable clé pour obtenir des performances élevées – voire presque illimitées – avec les applications Node.js consiste à exécuter plusieurs serveurs d’applications et à répartir la charge entre eux.
L’équilibrage de charge de Node.js peut s’avérer particulièrement délicat, car Node.js permet un niveau élevé d’interaction entre le code JavaScript exécuté dans le navigateur web et celui exécuté sur le serveur d’applications Node.js, les objets JSON servant de support à l’échange de données. Cela implique qu’une session client donnée s’exécute en permanence sur un serveur d’applications spécifique, et que la persistance des sessions est intrinsèquement difficile à assurer avec plusieurs serveurs d’applications.
L’un des principaux avantages d’Internet et du Web réside dans leur caractère largement sans état, qui permet notamment à n’importe quel serveur ayant accès au fichier demandé de répondre aux requêtes des clients. Node.js remet en cause ce modèle sans état et fonctionne mieux dans un environnement avec état, où un même serveur répond systématiquement aux requêtes de clients donnés.
Cette exigence peut être satisfaite au mieux par NGINX Plus, plutôt que par NGINX Open Source. Les deux versions de NGINX sont assez similaires, mais l’une des principales différences réside dans leur prise en charge de différents algorithmes d’équilibrage de charge.
NGINX prend en charge les méthodes d’équilibrage de charge sans état :
- Tourniquet – Une nouvelle requête est envoyée au serveur suivant dans la liste.
- Connexions minimales – Une nouvelle requête est envoyée au serveur qui compte le moins de connexions actives.
- Hachage IP – Une nouvelle requête est envoyée au serveur désigné par un hachage de l’adresse IP du client.
Une seule de ces méthodes, IP Hash, permet d’envoyer de manière fiable les requêtes d’un client donné vers le même serveur, ce qui est avantageux pour les applications Node.js. Cependant, IP Hash peut facilement conduire un serveur à recevoir un nombre disproportionné de requêtes au détriment des autres serveurs, comme l’explique cet article de blog sur les techniques d’équilibrage de charge. Cette méthode permet de conserver l’état, mais peut entraîner une répartition sous-optimale des requêtes entre les ressources des serveurs.
Contrairement à NGINX, NGINX Plus prend en charge la persistance de session. Lorsque la persistance de session est activée, toutes les requêtes d’un client donné sont systématiquement envoyées au même serveur. Cela permet de tirer pleinement parti à la fois des avantages de Node.js, grâce à sa communication avec état entre le client et le serveur, et de ceux de NGINX Plus, grâce à ses fonctionnalités avancées d’équilibrage de charge.
Vous pouvez donc utiliser NGINX ou NGINX Plus pour répartir la charge entre plusieurs serveurs Node.js. Toutefois, seul NGINX Plus vous permettra vraisemblablement de bénéficier à la fois de performances maximales en matière de répartition de charge et d’une gestion des états adaptée à Node.js. Les contrôles de l’état des applications et les fonctionnalités de surveillance intégrés à NGINX Plus sont également utiles dans ce contexte.
NGINX Plus prend également en charge la vidange des sessions, qui permet à un serveur d’applications de terminer proprement les sessions en cours après avoir demandé son propre retrait du service.
Conseil n° 4 – Mettre en proxy les connexions WebSocket
HTTP, dans toutes ses versions, est conçu pour les communications en mode « pull », dans lesquelles le client demande des fichiers au serveur. WebSocket permet les communications en mode « push » et « push/pull », dans lesquelles le serveur peut envoyer de manière proactive des fichiers que le client n’a pas demandés.
Le protocole WebSocket facilite des interactions plus robustes entre le client et le serveur, tout en réduisant la quantité de données transférées et en minimisant la latence. Si nécessaire, il est possible d’établir une connexion en duplex intégral, dans laquelle le client et le serveur peuvent tous deux envoyer et recevoir des requêtes selon les besoins.
Le protocole WebSocket dispose d’une interface JavaScript robuste et convient donc naturellement à Node.js en tant que serveur d’applications, mais aussi en tant que serveur web pour les applications web présentant des volumes de transactions modérés. Lorsque les volumes de transactions augmentent, il est judicieux d’intercaler NGINX entre les clients et le serveur web Node.js, en utilisant NGINX ou NGINX Plus pour mettre en cache les fichiers statiques et répartir la charge entre plusieurs serveurs d’applications.
Node.js est souvent utilisé conjointement avec Socket.IO, une API WebSocket devenue très populaire avec les applications Node.js. Cela peut entraîner une forte sollicitation du port 80 (pour HTTP) ou du port 443 (pour HTTPS). La solution consiste alors à utiliser un proxy vers le serveur Socket.IO. Vous pouvez utiliser NGINX comme serveur proxy, comme décrit ci-dessus, et bénéficier également de fonctionnalités supplémentaires telles que la mise en cache des fichiers statiques, la répartition de charge, et bien plus encore.

Voici le code d’un fichier d’application Node server.js qui écoute sur le port 5000. Il fait office de serveur proxy (et non de serveur web) et achemine les requêtes vers le port approprié :
var io = require('socket.io').listen(5000);
io.sockets.on('connection', function (socket) {
socket.on('set nickname', function (name) {
socket.set('nickname', name, function () {
socket.emit('ready');
});
});
socket.on('msg', function () {
socket.get('nickname', function (err, name) {
console.log('Chat message by ', name);
});
});
});Dans votre fichier index.html, ajoutez le code suivant pour vous connecter à votre application serveur et établir une connexion WebSocket entre l’application et le navigateur de l’utilisateur :
<script src="/socket.io/socket.io.js"></script><script>// <![CDATA[
var socket = io(); // your initialization code here.
// ]]>
</script>Pour obtenir des instructions complètes, notamment sur la configuration de NGINX, consultez notre article de blog consacré à l’utilisation de NGINX et NGINX Plus avec Node.js et Socket.IO. Pour en savoir plus sur les éventuels enjeux d’architecture et d’infrastructure liés aux applications web de ce type, consultez notre article de blog sur les applications web en temps réel et WebSocket.
Conseil n° 5 – Mettez en œuvre SSL/TLS et HTTP/2
De plus en plus de sites utilisent SSL/TLS pour sécuriser toutes les interactions des utilisateurs sur le site. C’est à vous de décider si et quand effectuer cette transition, mais si vous le faites, NGINX vous accompagne de deux manières :
- Une fois NGINX configuré en tant que proxy inverse, vous pouvez y terminer la connexion SSL/TLS avec le client. Le serveur Node.js échange alors des requêtes et du contenu non chiffrés avec le serveur proxy inverse NGINX.
- Les premières indications montrent que l’utilisation de HTTP/2, la nouvelle version du protocole HTTP, pourrait compenser en grande partie, voire totalement, la baisse de performances qu’entraîne habituellement l’utilisation de SSL/TLS. NGINX prend en charge HTTP/2 et vous permet de terminer les connexions HTTP/2 en même temps que les connexions SSL/TLS, évitant ainsi, là encore, toute modification des serveurs d’applications Node.js.
Parmi les étapes de mise en œuvre à suivre figurent la mise à jour de l’URL dans le fichier de configuration Node.js, l’établissement et l’optimisation de connexions sécurisées dans votre configuration NGINX, ainsi que l’utilisation de SPDY ou de HTTP/2 si vous le souhaitez. L’ajout de la prise en charge de HTTP/2 permet aux versions des navigateurs compatibles avec HTTP/2 de communiquer avec votre application à l’aide du nouveau protocole ; les versions plus anciennes continuent d’utiliser HTTP/1.x.

Le code de configuration suivant concerne un blog Ghost utilisant SPDY, comme décrit ici. Il inclut des fonctionnalités avancées telles que l’agrafage OCSP. Pour en savoir plus sur l’utilisation de NGINX pour la terminaison SSL, notamment sur l’option d’agrafage OCSP, consultez cette page ici. Pour une présentation générale des mêmes sujets, consultez cette page ici.
Il vous suffit d’apporter quelques modifications mineures pour configurer votre application Node.js et passer de SPDY à HTTP/2, dès maintenant ou lorsque la prise en charge de SPDY prendra fin début 2016.
server { server_name domain.com;
listen 443 ssl spdy;
spdy_headers_comp 6;
spdy_keepalive_timeout 300;
keepalive_timeout 300;
ssl_certificate_key /etc/nginx/ssl/domain.key;
ssl_certificate /etc/nginx/ssl/domain.crt;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 24h;
ssl_buffer_size 1400;
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/ssl/trust.crt;
resolver 8.8.8.8 8.8.4.4 valid=300s;
add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains';
add_header X-Cache $upstream_cache_status;
location / {
proxy_cache STATIC;
proxy_cache_valid 200 30m;
proxy_cache_valid 404 1m;
proxy_pass http://ghost_upstream;
proxy_ignore_headers X-Accel-Expires Expires Cache-Control;
proxy_ignore_headers Set-Cookie;
proxy_hide_header Set-Cookie;
proxy_hide_header X-powered-by;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Host $http_host;
expires 10m;
}
location /content/images {
alias /path/to/ghost/content/images;
access_log off;
expires max;
}
location /assets {
alias /path/to/ghost/themes/uno-master/assets;
access_log off;
expires max;
}
location /public {
alias /path/to/ghost/built/public;
access_log off;
expires max;
}
location /ghost/scripts {
alias /path/to/ghost/core/built/scripts;
access_log off;
expires max;
}
location ~ ^/(?:ghost|signout) {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://ghost_upstream;
add_header Cache-Control "no-cache, private, no-store,
must-revalidate, max-stale=0, post-check=0, pre-check=0";
proxy_set_header X-Forwarded-Proto https;
}
}
Conclusion
Cet article de blog présente certaines des principales optimisations de performances que vous pouvez apporter à vos applications Node.js. Il explique comment intégrer NGINX à votre architecture aux côtés de Node.js, en l’utilisant comme serveur proxy inverse, pour mettre en cache les fichiers statiques, répartir la charge, relayer les connexions WebSocket et assurer la terminaison des protocoles SSL/TLS et HTTP/2.
L’association de NGINX et de Node.js est largement reconnue comme un moyen de créer de nouvelles applications adaptées aux microservices ou d’apporter davantage de souplesse et de fonctionnalités aux applications existantes basées sur une architecture SOA et utilisant Java ou Microsoft .NET. Cet article vous aide à optimiser vos applications Node.js et, si vous le souhaitez, à concrétiser l’association de Node.js et de NGINX.
À propos de l’auteur

Articles de blog associés

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 ?
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
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
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
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
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.