SSD Nodes Learn 🎉 VPS dès $5.50/mois
Guides Matt ConnorPar Matt Connor

Configuration nginx reverse proxy expliquée simplement

Configurez un reverse proxy nginx ligne par ligne sur Ubuntu 24.04 : proxy_pass, quatre headers, websockets, trailing slashes et limite des uploads.

Ce que fait une configuration de reverse proxy nginx

Un reverse proxy nginx reçoit les requêtes arrivant sur les ports 80 et 443. Il transmet chacune d’elles à une application qui écoute déjà sur un port local, puis renvoie la réponse de cette application au navigateur. La configuration tient dans un seul bloc server, qui reste court. La difficulté se concentre presque entièrement dans cinq ou six lignes. Elles indiquent à votre application l’identité réelle du client et le protocole utilisé par celui-ci.

Tout ce qui suit est construit à partir de rien sur Ubuntu 24.04, avec le paquet nginx fourni par la distribution. Au départ, une application répond déjà sur 127.0.0.1:3000. Si vous n’avez pas encore choisi de proxy, consultez d’abord la comparaison entre nginx, Caddy et Traefik. La suite présente la configuration nginx, ligne par ligne.

Exécutez ces configurations sur votre propre serveur. Testez chaque modification avec sudo nginx -t avant de recharger nginx, puis lisez le résultat affiché.

Emplacement de la configuration de nginx sur Ubuntu

sudo apt update
sudo apt install -y nginx
ls -l /etc/nginx/sites-enabled/

Le fichier principal est /etc/nginx/nginx.conf. Il définit les options globales dans un bloc http { }, puis inclut deux répertoires : /etc/nginx/conf.d/*.conf et /etc/nginx/sites-enabled/*. Sur Ubuntu et Debian, vous créez un fichier par site dans /etc/nginx/sites-available/, puis vous l’activez avec un lien symbolique dans /etc/nginx/sites-enabled/. Supprimer le lien symbolique désactive le site tout en conservant le fichier.

Deux directives utilisées plus loin ne fonctionnent que dans le contexte http, jamais dans un bloc server : map et upstream. Placez-les dans leur propre fichier sous /etc/nginx/conf.d/, car ce répertoire est inclus au niveau http.

Le paquet fournit un site activé nommé default. Il est marqué default_server, ce qui signifie qu’il répond à toute requête dont l’en-tête Host ne correspond à aucun server_name dans votre configuration. Tant qu’il reste activé, une requête qui ne correspond pas à vos noms arrive sur ce site au lieu de votre application. Supprimez le lien symbolique une fois que votre propre site fonctionne.

sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx

Le bloc server minimal qui relaie une application

server {
    listen 80;
    listen [::]:80;
    server_name app.example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
    }
}

Enregistrez-le sous /etc/nginx/sites-available/app.example.com, puis activez-le et chargez-le.

sudo ln -s /etc/nginx/sites-available/app.example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
curl -sI -H 'Host: app.example.com' http://127.0.0.1/

listen 80; écoute sur IPv4 et listen [::]:80; écoute sur IPv6. Si vous omettez la deuxième ligne et que la résolution DNS (domain name system) du visiteur renvoie un enregistrement AAAA pour votre serveur, la connexion est refusée, tandis que le site fonctionne pour tous les utilisateurs en IPv4. Le rapport d’incident que vous recevez indique : « chez moi, ça fonctionne ».

server_name est comparé à l’en-tête Host envoyé par le navigateur. Vous pouvez indiquer plusieurs noms, séparés par des espaces. Si aucun bloc ne correspond, nginx utilise le bloc default_server. C’est pourquoi le site fourni par le paquet devait être supprimé.

location / effectue une correspondance par préfixe sur le chemin de la requête, tandis que / correspond à tous les chemins. proxy_pass est l’adresse à laquelle nginx ouvre une connexion. Laissez l’application liée à 127.0.0.1 afin que le seul accès passe par nginx. Si l’application s’exécute dans un conteneur, publiez-la sur 127.0.0.1:3000:3000 et non sur 3000:3000, car Docker écrit ses propres règles et publie les ports directement en dehors d’ufw, ce qui rend un port publié sans restriction accessible depuis Internet, quelle que soit la configuration de votre pare-feu.

La ligne curl envoie le bon en-tête Host depuis le serveur lui-même. Vous pouvez ainsi tester le bloc avant que le DNS pointe vers le serveur.

Ce que nginx envoie en amont lorsque vous ne configurez rien d’autre

proxy_pass utilisé seul masque quatre éléments à votre application.

Par défaut, nginx utilise HTTP/1.0 pour communiquer avec le backend et envoie Connection: close. Chaque requête ouvre donc une nouvelle connexion en amont, et aucune mise à niveau du protocole n’est possible.

L’en-tête Host est réécrit avec la valeur de proxy_pass, à savoir 127.0.0.1:3000. Une application qui génère des liens absolus à partir de Host produit alors des liens que personne en dehors du serveur ne peut ouvrir.

La connexion reçue par l’application provient de nginx. L’application voit donc l’adresse du client 127.0.0.1. Chaque ligne de journal et chaque limite de débit appliquée dans l’application enregistre alors le proxy au lieu du visiteur.

L’application ne peut pas savoir que le navigateur a utilisé HTTPS, car la connexion qu’elle reçoit est une connexion HTTP simple sur une adresse loopback.

Quatre lignes suffisent à corriger tout cela.

Les quatre en-têtes à définir et les informations qu’ils transmettent au backend

location / {
    proxy_pass http://127.0.0.1:3000;

    proxy_set_header Host              $host;
    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 $scheme;
}

Host transmet le nom saisi par le visiteur. $host est le nom de la requête, sans le port et converti en minuscules. Définissez-le pour que votre application construise des URL absolues correctes : la redirection après une connexion ou le lien contenu dans un e-mail de réinitialisation du mot de passe. Si vous l’omettez, ces URL pointent vers 127.0.0.1:3000, et la connexion redirige le navigateur vers une adresse qui refuse la connexion. Si votre application a aussi besoin du port, parce que vous la servez sur 8080, utilisez $http_host, qui correspond exactement à l’en-tête envoyé par le client.

X-Real-IP transmet une seule valeur : $remote_addr, l’adresse depuis laquelle nginx a accepté la connexion. Les applications la lisent pour leurs propres journaux d’accès et leur propre limitation de débit.

X-Forwarded-For transmet une liste. $proxy_add_x_forwarded_for ajoute $remote_addr à la valeur déjà placée par le client dans cet en-tête. La valeur est donc séparée par des virgules et l’entrée ajoutée par nginx est la dernière. Ce détail détermine si l’en-tête est fiable : un client peut envoyer n’importe quel X-Forwarded-For, et une application qui lit la première entrée peut donc recevoir n’importe quelle adresse. Lorsque nginx est le serveur en périphérie, écrivez plutôt $remote_addr et ignorez la valeur envoyée par le client. Lorsqu’un CDN ou un autre proxy est placé devant, utilisez set_real_ip_from et real_ip_header du module realip afin que $remote_addr devienne l’adresse réelle du client.

X-Forwarded-Proto transmet http ou https. Les frameworks les lisent pour déterminer s’ils doivent marquer les cookies Secure et s’il faut forcer une redirection vers HTTPS. Si vous l’omettez sur un site TLS et qu’une application configurée pour forcer HTTPS reçoit http, elle répond par une redirection vers l’adresse HTTPS, reçoit la requête suivante via nginx, voit encore http et redirige à nouveau. Le navigateur abandonne et affiche ERR_TOO_MANY_REDIRECTS.

Répéter ces quatre lignes dans chaque location entraîne leur divergence. Placez-les dans un seul fichier et incluez ce fichier.

# /etc/nginx/snippets/proxy-headers.conf
proxy_set_header Host              $host;
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 $scheme;
location / {
    include snippets/proxy-headers.conf;
    proxy_pass http://127.0.0.1:3000;
}

L’héritage comporte ici un piège. Une location hérite des directives proxy_set_header de son bloc server uniquement si elle ne définit aucune directive de ce type. Ajoutez un seul proxy_set_header dans la location et tous les en-têtes définis au niveau du serveur sont supprimés pour cette location. Placez donc toutes les directives au même niveau ou include l’extrait dans chaque location qui utilise un proxy.

Pourquoi mon application WebSocket se connecte-t-elle, puis se déconnecte-t-elle ?

Les valeurs par défaut interdisent la mise à niveau, et le délai de lecture par défaut ferme un tunnel inactif après 60 secondes. Une WebSocket commence par une requête HTTP contenant Upgrade: websocket et Connection: Upgrade. Ce sont des en-têtes hop-by-hop. Le proxy doit donc les consommer au lieu de les transmettre. De plus, HTTP/1.0 ne possède aucun mécanisme de mise à niveau. Les deux éléments doivent être réinjectés manuellement.

La map doit se trouver dans le contexte http, dans son propre fichier.

# /etc/nginx/conf.d/websocket.conf
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

Ensuite, ajoutez la location.

location / {
    include snippets/proxy-headers.conf;
    proxy_pass http://127.0.0.1:3000;

    proxy_http_version 1.1;
    proxy_set_header Upgrade    $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    proxy_read_timeout 3600s;
    proxy_send_timeout 3600s;
}

La map permet à une seule location de gérer les deux types de trafic. Pour une requête ordinaire, $http_upgrade est vide. $connection_upgrade devient donc close. Pour une requête de mise à niveau, elle contient websocket. L’en-tête envoyé au backend devient alors Connection: upgrade. Définir directement proxy_set_header Connection "upgrade"; envoie aussi cet en-tête pour toutes les requêtes de pages ordinaires. Certains backends répondent alors par une erreur 400.

proxy_read_timeout explique les messages du type « la page se charge, puis elle ne se met plus à jour ». Sa valeur par défaut est de 60 secondes. Elle mesure l’intervalle entre deux lectures depuis le backend, et non la durée de vie de la connexion. Une WebSocket qui reste silencieuse pendant 60 secondes est fermée par nginx. La console du navigateur affiche alors la fermeture de la socket avec le code 1006. Les applications qui envoient leur propre heartbeat plus souvent qu’une fois par minute ne rencontrent pas ce problème. Les autres s’arrêtent au bout d’une minute. Les éditeurs en temps réel et les dashboards sont les premiers concernés, une instance n8n auto-hébergée derrière HTTPS étant un exemple courant.

Pourquoi un slash final dans proxy_pass modifie-t-il mes URL ?

La règle tient en une phrase. Si proxy_pass se termine par un URI (identifiant uniforme de ressource), même par un simple /, nginx supprime la partie du chemin de la requête qui correspondait au préfixe location, puis la remplace par cet URI. Si proxy_pass s’arrête au niveau de l’hôte et du port, le chemin de la requête est transmis sans modification.

location /app/ {
    proxy_pass http://127.0.0.1:3000/;
}

Une requête vers /app/status atteint le backend sous la forme /status.

location /app/ {
    proxy_pass http://127.0.0.1:3000;
}

Une requête vers /app/status atteint le backend sous la forme /app/status.

La forme à choisir dépend de l’application. Une application configurée avec un base-path ou un sous-dossier attend la deuxième forme, et ce paramètre doit connaître /app. Une application qui ne gère pas les préfixes nécessite la première. Cette première forme a un effet immédiat : le HTML renvoyé par l’application contient toujours des chemins absolus comme /static/main.css, le navigateur les demande à la racine du site, aucune location ne correspond et la page s’affiche sans style. Dans l’onglet réseau du navigateur, ces requêtes vers les ressources renvoient un code 404. La solution consiste à configurer le base-path de l’application ou à ajouter une deuxième location /static/ vers le même backend.

Une location utilisant une expression régulière ne peut pas contenir d’URI dans proxy_pass. sudo nginx -t refuse la configuration et indique la raison : "proxy_pass" cannot have URI part in location given by regular expression, or inside named location, or inside "if" statement, or inside "limit_except" block.

Toute cette catégorie de problèmes disparaît lorsque chaque application possède son propre nom, app.example.com, et est proxyfiée depuis location /. Les sous-chemins ne valent la complexité supplémentaire que lorsque vous ne pouvez pas ajouter d’enregistrements DNS.

Comment placer plusieurs backends derrière un même nom ?

Avec un bloc upstream. Il appartient au contexte http. Écrivez-le donc au-dessus du bloc server dans le même fichier, ou dans /etc/nginx/conf.d/.

upstream app_backend {
    least_conn;
    server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:3001 max_fails=3 fail_timeout=30s;
    keepalive 32;
}

La directive location le référence ensuite : proxy_pass http://app_backend;.

La méthode par défaut est le round robin. least_conn envoie chaque requête au backend qui possède le moins de connexions actives. Cette méthode convient aux requêtes dont la durée varie. ip_hash associe une adresse client à un backend précis. Vous en avez besoin avec ip_hash lorsque l’application conserve les sessions dans sa propre mémoire. Avec deux backends de ce type, le round robin déconnecte aléatoirement les utilisateurs lorsque leurs requêtes arrivent sur l’instance qui ne les a jamais vus. Le stockage partagé des sessions est une meilleure solution.

max_fails=3 fail_timeout=30s signifie que trois échecs en 30 secondes retirent ce serveur du pool pendant 30 secondes. Lorsque tous les serveurs du bloc sont dans cet état, les clients reçoivent une erreur 502 et le journal d’erreurs indique no live upstreams while connecting to upstream.

keepalive 32 maintient ouvertes jusqu’à 32 connexions inactives vers les backends par processus worker. Cela évite une négociation TCP pour la plupart des requêtes. Cette directive fonctionne uniquement avec proxy_http_version 1.1 et sans directive Connection: close vers le backend. Si le même location utilise également la map WebSocket, remplacez le cas vide `close par une chaîne vide. Les requêtes ordinaires n’enverront ainsi aucun en-tête Connection` et la connexion du pool pourra être réutilisée.

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      '';
}

Les noms placés dans un bloc upstream sont résolus au démarrage de nginx. Si votre backend est un conteneur qui reçoit une nouvelle adresse lors de son redémarrage, nginx continue d’utiliser l’ancienne adresse jusqu’à son rechargement. Dans un réseau Docker, vous pouvez déplacer la résolution au moment de la requête avec le resolver intégré.

resolver 127.0.0.11 valid=10s;
set $backend http://app:3000;
proxy_pass $backend;

Lorsque les conteneurs apparaissent et disparaissent assez souvent pour vous obliger à modifier régulièrement nginx, un proxy qui lit les labels des conteneurs est un meilleur choix. Traefik devant plusieurs applications Docker Compose construit ses routes à partir des conteneurs eux-mêmes.

Pourquoi les téléversements échouent-ils avec l’erreur 413 Request Entity Too Large ?

client_max_body_size vaut 1 megabyte par défaut. nginx refuse un corps de requête plus volumineux avant que votre application n’en reçoive la moindre partie, et le journal d’erreurs enregistre client intended to send too large body. Augmentez cette valeur dans le bloc server ou dans le bloc location qui gère les téléversements.

client_max_body_size 512m;

La valeur 0 désactive complètement ce contrôle. L’application a également sa propre limite. Si l’erreur 413 persiste après cette modification, elle vient du backend. Consultez alors le paramètre de téléversement de l’application.

Par défaut, nginx lit l’intégralité du corps de la requête avant d’ouvrir la connexion vers l’upstream. Il écrit d’abord les données volumineuses dans un fichier temporaire sur disque. L’application est ainsi protégée contre les clients lents, car le backend reçoit le téléversement à la vitesse du réseau local. Pour les téléversements très volumineux, vous pouvez utiliser le streaming.

proxy_request_buffering off;

Le backend reçoit alors le corps au fur et à mesure de son arrivée et doit être capable de le gérer. nginx ne peut plus réessayer la requête auprès d’un autre upstream, car le corps a déjà été consommé.

client_body_timeout, dont la valeur par défaut est de 60 seconds, s’applique entre deux lectures successives du corps et non à l’ensemble du téléversement. Un téléversement lent mais régulier continue. Un téléversement bloqué est interrompu.

Mise en mémoire tampon des réponses et réglage qui bloque la sortie en temps réel

proxy_buffering est activé par défaut et c’est généralement le comportement souhaité. nginx lit la réponse de votre application aussi vite que celle-ci peut l’écrire, la conserve en mémoire, puis l’envoie à un client lent au rythme de ce dernier. Le worker de l’application termine rapidement au lieu de rester occupé pendant toute la durée du téléchargement lent.

Cela bloque les réponses en streaming. Les Server-Sent Events et la sortie en temps réel des journaux n’affichent rien au lecteur tant qu’un buffer n’est pas rempli. Désactivez la mise en mémoire tampon uniquement dans cet emplacement.

proxy_buffering off;

Si vous contrôlez l’application, il est préférable d’envoyer l’en-tête X-Accel-Buffering: no uniquement avec les réponses en streaming. nginx lit cet en-tête pour chaque réponse et désactive la mise en mémoire tampon pour celle-ci uniquement. Les pages classiques conservent ainsi cet avantage.

Lorsque le journal d’erreurs contient upstream sent too big header while reading response header from upstream, les en-têtes de réponse ne tiennent pas dans un seul buffer. proxy_buffer_size correspond par défaut à une seule page mémoire, soit 4 ou 8 kilobytes selon la plateforme. Les cookies longs ou les en-têtes d’authentification volumineux dépassent cette taille. Augmentez les deux valeurs.

proxy_buffer_size 16k;
proxy_buffers 8 16k;

Où placer TLS dans cette configuration ?

Dans Nginx, devant tous les éléments précédents. TLS (Transport Layer Security) se termine sur le proxy, et la connexion entre Nginx et l’application reste en HTTP simple via l’adresse loopback, où aucun autre système du réseau ne peut la lire. L’application apprend que le visiteur a utilisé HTTPS grâce à X-Forwarded-Proto, le quatrième des quatre en-têtes.

Ne renseignez pas manuellement les chemins des certificats. Pointez l’enregistrement DNS vers le serveur, ouvrez le firewall, puis laissez Certbot modifier ce même server block : il ajoute la ligne listen 443 ssl avec les chemins ssl_certificate, ainsi qu’une redirection depuis le port 80. Émettre un certificat Let's Encrypt pour Nginx avec Certbot explique l’émission du certificat et le timer de renouvellement.

sudo ufw allow 'Nginx Full'
sudo ufw status

Nginx Full est un profil d’application installé par le paquet Nginx. Il ouvre simultanément les ports 80 et 443. Le port 80 doit rester ouvert pour le challenge de renouvellement HTTP-01, même après la redirection de tous les visiteurs vers HTTPS.

Tester la configuration, puis recharger

sudo nginx -t
sudo systemctl reload nginx

nginx -t analyse chaque fichier inclus et indique soit que le test a réussi, soit le fichier et la ligne où il s’est arrêté. Lisez cette sortie avant de recharger. Un rechargement avec une configuration incorrecte n’est pas appliqué : nginx continue de servir l’ancienne configuration. Le site reste donc accessible, tandis que votre modification ne produit aucun effet. systemctl restart se comporte différemment, et de manière plus problématique, car un redémarrage arrête d’abord le serveur en cours d’exécution. Une erreur de configuration laisse alors nginx complètement arrêté. Utilisez le rechargement par défaut et réservez le redémarrage aux rares modifications qui l’exigent.

sudo tail -f /var/log/nginx/error.log
sudo ss -lntp | grep -E ':(80|443|3000)'

La ligne ss indique quel processus utilise chaque port. Vous pouvez ainsi vérifier que l’application écoute bien à l’emplacement indiqué par proxy_pass.

Les pannes que vous rencontrerez réellement

502 Bad Gateway, avec connect() failed (111: Connection refused) while connecting to upstream dans le journal d’erreurs. Rien n’écoute à l’adresse indiquée dans proxy_pass. L’application est arrêtée, elle est liée à un autre port ou elle est liée à une adresse interne au conteneur que l’hôte ne peut pas atteindre.

502 avec no live upstreams while connecting to upstream. Tous les serveurs du bloc upstream sont actuellement marqués comme défaillants par max_fails. Réparez les backends. nginx effectue une nouvelle tentative lorsque fail_timeout expire.

504 Gateway Time-out, avec upstream timed out (110: Connection timed out) while reading response header from upstream. Le backend a accepté la connexion, puis n’a rien envoyé pendant proxy_read_timeout secondes. Augmenter le délai d’attente convient pour un rapport réellement lent, mais pas pour une application bloquée.

Chaque chemin renvoie une erreur 404 depuis l’application. La règle concernant le slash final a réécrit le chemin. Comparez le chemin indiqué dans les journaux de l’application avec celui que vous avez demandé.

Un autre site répond. server_name ne correspond pas à l’en-tête Host. La requête est donc passée dans le bloc default_server.

La page se charge, puis l’interface se fige après environ une minute. Il s’agit du cas WebSocket : la gestion de Upgrade est absente, ou proxy_read_timeout vaut encore 60 secondes.

FAQ

Pourquoi nginx renvoie-t-il 502 Bad Gateway après l’ajout de proxy_pass ?

nginx n’a pas pu ouvrir de connexion vers l’adresse indiquée dans proxy_pass. Le journal d’erreurs à l’emplacement /var/log/nginx/error.log indique la cause : connect() failed (111: Connection refused) while connecting to upstream signifie qu’aucun processus n’écoute à cette adresse, tandis que no live upstreams signifie que tous les serveurs d’un bloc upstream ont été marqués comme défaillants. Exécutez sudo ss -lntp | grep 3000 pour voir quel processus utilise le port et sur quelle adresse il est lié. Une application liée à une adresse interne au conteneur, ou à un autre port que celui indiqué dans la configuration, provoque systématiquement cette erreur.

Pourquoi mon application se déconnecte-t-elle après environ une minute derrière nginx ?

La connexion utilise WebSocket et proxy_read_timeout conserve sa valeur par défaut de 60 secondes. Ce délai mesure l’intervalle entre deux lectures depuis le backend. nginx ferme une socket inactive et la console du navigateur signale le code de fermeture 1006. Définissez proxy_http_version 1.1, transmettez Upgrade et Connection avec un map sur $http_upgrade, puis augmentez proxy_read_timeout à une valeur telle que 3600s. Sans l’en-tête Upgrade, la mise à niveau n’a jamais lieu. L’application revient alors à l’interrogation périodique ou n’affiche plus les mises à jour en temps réel.

La barre oblique finale dans proxy_pass est-elle importante ?

Oui. Elle modifie le chemin reçu par le backend. Avec location /app/ et proxy_pass http://127.0.0.1:3000/, une requête vers /app/status arrive sur le backend sous la forme /status, car tout URI placé après l’hôte et le port remplace le préfixe de l’emplacement correspondant. Supprimez cette barre oblique finale : la même requête arrive alors sous la forme /app/status. La suppression du préfixe casse souvent les liens vers les ressources de l’application. Ces liens restent absolus et renvoient ensuite 404 à la racine du site. Pour une application configurée avec un chemin de base, utilisez plutôt la forme qui transmet le chemin.

Pourquoi les journaux de mon application indiquent-ils 127.0.0.1 comme adresse IP de chaque visiteur ?

Parce que la connexion reçue par l’application provient réellement de nginx, via l’adresse loopback. L’adresse du visiteur n’est transmise à l’application que dans un en-tête que vous configurez : proxy_set_header X-Real-IP $remote_addr; pour une valeur unique et proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; pour la chaîne complétée. L’application doit ensuite être configurée pour faire confiance à ces en-têtes. N’oubliez pas qu’un client peut envoyer son propre X-Forwarded-For. Lorsque nginx est le serveur edge, remplacez donc cette valeur par $remote_addr au lieu de l’ajouter.

Ai-je besoin de TLS sur la connexion entre nginx et mon application ?

Non, si l’application s’exécute sur le même serveur et est liée à 127.0.0.1, car ce trafic ne quitte jamais la machine. Terminez TLS sur nginx, gardez proxy_pass en HTTP non chiffré via loopback et envoyez X-Forwarded-Proto $scheme pour que l’application sache que le visiteur a utilisé HTTPS. Si le backend se trouve sur un autre hôte, via un réseau que vous ne contrôlez pas, cette liaison doit être protégée séparément : utilisez HTTPS vers le backend ou un tunnel privé entre les deux machines.