Les en-têtes transférés ne constituent pas une entrée fiable : X-Forwarded-For, X-Forwarded-Proto et X-Forwarded-Host derrière un tunnel
Derrière un tunnel, X-Forwarded-For, X-Forwarded-Proto et X-Forwarded-Host constituent des entrées non fiables. Découvrez les bons réglages trustProxy pour Express, Fastify, Django et Rails.

Quick answer
En-têtes transférés non fiables derrière un tunnel: quick answer
If free tunnel limits interrupt your workflow, compare session length, stable URLs, concurrent tunnels, and paid-plan pricing before choosing a localhost tunnel tool.
What free tunnel limits should developers check first?
Check session duration, URL stability, concurrent tunnels, custom subdomains, bandwidth or request limits, and whether webhook callbacks survive restarts.
How does InstaTunnel handle longer development sessions?
InstaTunnel Free is designed around 24-hour sessions, with Pro available for higher limits and MCP endpoint tunnel workflows.
Lorsque vous placez une application derrière un tunnel, trois en-têtes de requête commencent à influencer ce que votre code croit. X-Forwarded-For indique qui appelle, X-Forwarded-Proto indique si la requête était HTTPS, et X-Forwarded-Host indique quel nom d’hôte le demandeur a demandé. Configurer la confiance de manière incorrecte peut provoquer l’un des deux échecs. Le plus innocent est une boucle de redirection, ou une limitation de débit qui ne voit qu’un seul visiteur. Le plus dangereux est un attaquant qui choisit les valeurs sur lesquelles reposent vos contrôles de sécurité.
2026 a rendu la seconde défaillance concrète. Fastify a publié deux CVE en cinq mois sur ce problème exact, et le paquet proxy-addr derrière le req.ip d’Express avait son propre bug critique de sous-réseau de confiance. Ce guide explique ce que transmet réellement un tunnel, pourquoi les applications le lisent mal, et les réglages qui fonctionnent pour Express, Fastify, Django et Rails.
Ce que transmet réellement un tunnel
Un tunnel modifie la forme de la connexion. La session TLS du visiteur se termine à la bordure du fournisseur. La bordure transmet la requête à travers le tunnel vers un agent sur votre machine, et l’agent effectue une requête HTTP en clair vers votre application sur localhost. Votre application ne voit jamais la socket du visiteur. La connexion qu’elle voit provient de l’agent, généralement 127.0.0.1, et tout le reste concernant le visiteur arrive sous forme d’en-têtes.
La documentation du fournisseur décrit ces en-têtes :
- ngrok ajoute l’IP client à
X-Forwarded-Foret le schéma de la requête àX-Forwarded-Proto. Si le visiteur a déjà envoyé l’un des en-têtes, ngrok ajoute une nouvelle valeur au lieu de remplacer l’ancienne, et sa documentation vous indique de lire la dernière valeur. PourX-Forwarded-Host, ngrok conserve l’en-tête du visiteur lorsqu’il existe et, sinon, utilise leHostde la requête. - Cloudflare envoie l’adresse du visiteur dans
CF-Connecting-IPet la recommande plutôt queX-Forwarded-For. Elle ajoute l’adresse de connexion à toutX-Forwarded-Forexistant, de sorte que les valeurs tapées par le visiteur restent à gauche. Un problème public decloudflaredmontre une requête avec une liste falsifiée arrivant à l’origine avec l’adresse réelle ajoutée à la fin.
Le schéma est le même partout : les bords ajoutent, ils ne nettoient pas. Tout ce que le visiteur a saisi est encore présent dans l’en-tête que votre application reçoit. Seuls les éléments ajoutés par les sauts que vous contrôlez peuvent être considérés comme fiables.
Pourquoi votre limiteur de taux voit l’IP du tunnel
La plupart des limiteurs de taux se basent sur l’adresse de socket. Derrière un tunnel, chaque requête provient de l’agent sur localhost, donc chaque visiteur tombe dans le même seau. Un client bruyant épuise le quota pour tout le monde, et une liste blanche IP bloque tout le trafic ou l’autorise entièrement.
La solution consiste à indiquer à votre framework quelle adresse de connexion est un proxy de confiance, afin qu’il puisse lire en sécurité l’adresse du client dans X-Forwarded-For. La mauvaise solution consiste à lire vous-même la première valeur de l’en-tête. Les conseils de MDN sont sans équivoque : toute utilisation sécurisée de X-Forwarded-For, telle que la limitation de débit ou le contrôle d’accès par IP, doit n’utiliser que les adresses ajoutées par un proxy de confiance, et les valeurs les plus à gauche ne conviennent que là où le spoofing n’a aucun impact. Un problème Go qui a étudié les limiteurs de taux a trouvé la même erreur répétée, avec l’analyse de la valeur la plus à gauche permettant aux clients d’échapper à leurs limites.
Pourquoi une détection de protocole incorrecte provoque des boucles de redirection
TLS se termine à l’edge, donc votre application reçoit du HTTP en clair. Si elle force HTTPS, elle voit une requête non sécurisée et répond par une redirection vers https://. Le navigateur suit, la requête repasse par l’edge, et votre application voit à nouveau du HTTP en clair. La boucle ne se termine jamais.
La documentation de Django dit ceci clairement : si SECURE_SSL_REDIRECT provoque des redirections infinies, le site ne peut probablement pas déterminer quelles requêtes sont sécurisées derrière un proxy, et SECURE_PROXY_SSL_HEADER est la solution. Rails décrit le même piège, notant que derrière un proxy qui termine le SSL, la requête transférée ressemble à HTTP, ce qui expédie les redirections et les sécurités des cookies vers le mauvais schéma.
Avant de modifier un paramètre, affichez ce qui arrive réellement. Une publication de 2024 a constaté que X-Forwarded-Proto restait en http à travers un tunnel Cloudflare et utilisait l’en-tête CF-Visitor à la place. Les fournisseurs modifient le comportement, vérifiez donc avec votre propre requête.
La règle : faire confiance à la connexion, puis à l’en-tête
Les en-têtes X-Forwarded-* sont des affirmations, pas des faits. MDN indique que si un serveur peut être atteint directement depuis Internet, même s’il est aussi derrière un proxy de confiance, aucune partie de la liste X-Forwarded-For n’est fiable. L’algorithme sûr comporte deux étapes :
- Vérifiez si l’adresse de connexion appartient à un proxy que vous faites confiance.
- Puis lisez l’en-tête du côté droit, en ignorant les proxys de confiance. La première adresse non fiable est le client.
Les frameworks qui sautent l’étape 1, ou qui l’implémentent mal, sont ceux qui ont conduit aux avis 2026.
Le fil des CVE de 2026
CVE-2026-3635 (Fastify, publié le 23 mars 2026). Dans Fastify 5.8.2 et les versions antérieures, une valeur restrictive de trustProxy, telle qu’une IP, un sous-réseau, un nombre de sauts ou une fonction personnalisée, laisse tout de même request.protocol et request.host lire X-Forwarded-Proto et X-Forwarded-Host depuis n’importe quelle connexion, y compris les non-fiables. Un attaquant atteignant le serveur directement pourrait usurper les deux. L’avis liste l’application HTTPS, les drapeaux de cookies sécurisés, les contrôles d’origine CSRF, la construction d’URL et le routage basé sur l’hôte comme affectés. Le score CVSS est 6.1, et la correction est Fastify 5.8.3. Avec trustProxy: true le comportement est attendu, car ce réglage fait confiance à tout le monde.
CVE-2026-16732 (Fastify, publié le 18 août 2026). La vérification 5.8.3 vérifie l’adresse de connexion, ce qui fonctionne pour les formes IP, CIDR et fonction. La forme numérique de comptage de sauts se compile en un prédicat qui ignore l’adresse, donc la vérification passait toujours. Les versions 5.8.3 jusqu’à 5.12.0 demeuraient vulnérables. L’impact correspond à la CVE parente et ajoute une corruption du cache. Fastify 5.12.1 désactive trustProxy numérique à l’exécution et le retire des types TypeScript. La solution proposée par l’avis est une valeur IP, CIDR ou fonction, et assurez-vous que l’origine est atteignable uniquement via la chaîne de proxies.
CVE-2026-90711 (proxy-addr, publié le 15 septembre 2026). proxy-addr détermine les adresses client derrière des proxys de confiance et soutient les req.ip et req.ips d’Express. Dans les versions 1.1.0 à 2.0.7, un sous-réseau de confiance écrit sous forme IPv4-mappée IPv6 avec un préfixe de taille IPv4, comme ::ffff:10.0.0.0/8 au lieu de ::ffff:10.0.0.0/104, était accepté silencieusement et faisait confiance à chaque adresse IPv4. Tout client pouvait alors fournir un X-Forwarded-For arbitraire, contournant le contrôle d’accès IP, la limitation de débit, la géolocalisation et les journaux d’audit. Le score CVSS est 9.1, et la correction est 2.0.8. Le plugin Fastify l’a héritée comme CVE-2026-92395, affectant @fastify/proxy-addr de 3.0.0 à 5.1.0, corrigé dans 5.1.1.
Les avis partagent une précondition : l’attaquant doit atteindre votre application sans passer par le proxy. Sur un tunnel, cela semble improbable jusqu’à ce que vous vérifiiez comment votre serveur de dev est lié. Un serveur écoutant sur 0.0.0.0, ou un conteneur avec un port publié, est accessible depuis votre réseau local sans tunnel impliqué. C’est une inférence tirée du fonctionnement du binding, et non quelque chose que disent les avis, mais cela explique pourquoi « ce n’est qu’un tunnel de dev » est une maigre consolation. Lie-le à 127.0.0.1.
Réglages de confiance corrects par framework
Express
Express par défaut met trust proxy à false, donc req.ip est l’adresse du socket et chaque visiteur ressemble à l’agent. Le régler sur true corrige cela et crée un problème différent : Express considère alors l’entrée la plus à gauche de X-Forwarded-For comme le client, et sa documentation avertit que le dernier proxy de confiance doit écraser les en-têtes de forwarding ou que les clients peuvent fournir n’importe quelle valeur. Comme les tunnels ajoutent, true est le mauvais choix.
Faites confiance à l’adresse de l’agent à la place :
// Agent runs on the same machine
app.set('trust proxy', 'loopback');
// Agent in a container: trust that network only
app.set('trust proxy', ['loopback', '172.18.0.0/16']);
Express lit X-Forwarded-For à partir du droit uniquement lorsque le pair du socket est de confiance. Avec ce réglage, req.protocol suit X-Forwarded-Proto, req.hostname suit X-Forwarded-Host, et req.ip est la première adresse non fiable à partir de la droite. Passez à proxy-addr vers 2.0.8 ou plus tard (npm ls proxy-addr montre quelle copie vous avez) et revérifiez les sous-réseaux IPv4-mappés IPv6.
Fastify
Passez à 5.12.1 ou version ultérieure, et arrêtez d’utiliser des valeurs numériques :
const fastify = require('fastify')({
trustProxy: ['127.0.0.1', '::1'] // IPs or CIDRs of the agent only
});
La documentation actuelle de Fastify classe aussi request.ip, request.host et request.protocol comme métadonnées de requête à traiter comme entrées non fiables. Attachez le serveur à loopback afin que la vérification ne puisse pas être contournée.
Django
Django n’a pas d’équivalent à trust proxy pour les adresses IP des clients. REMOTE_ADDR est le pair du socket, vous avez donc besoin d’un choix délibéré. Deux réglages comptent pour le schéma :
# settings.py
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SECURE_SSL_REDIRECT = True
Django s’avère qu’il faut: activez ceci uniquement lorsque le proxy supprime X-Forwarded-Proto des requêtes entrantes, y compris les valeurs arrivant sous forme de listes séparées par des virgules. Un tunnel qui ajoute plutôt qu’il ne remplace ne satisfait pas cette exigence, vérifiez donc avec le test d’en-tête ci-dessous. Au niveau du serveur, Gunicorn fait confiance uniquement aux en-têtes transférés depuis 127.0.0.1 par défaut via forwarded_allow_ips, et l’option --proxy-headers de uvicorn est activée par défaut mais limitée à --forwarded-allow-ips, qui par défaut est 127.0.0.1. Les configurations de conteneurs incitent les gens à mettre *; énumérez plutôt le réseau de l’agent. Pour les adresses IP des clients, django-ipware prend en charge proxy_trusted_ips et proxy_count pour exactement cela.
Rails
ActionDispatch::RemoteIp retire les proxys de confiance de la chaîne transférée et renvoie la dernière adresse qui reste. Sa liste de proxys de confiance par défaut couvre les boucles locales et les plages privées, ce qui convient à un agent de tunnel sur localhost. Elle déclenche aussi IpSpoofAttackError lorsque Client-Ip et X-Forwarded-For sont en désaccord. Si vous définissez config.action_dispatch.trusted_proxies, passez un tableau, car un itérable remplace les valeurs par défaut.
Pour la boucle de redirection, Rails propose config.assume_ssl = true, qui indique au serveur que le proxy a déjà terminé le SSL. Utilisez-le uniquement lorsque l’application est accessible exclusivement via le tunnel, car ce réglage considère chaque requête comme HTTPS.
Testez comme un attaquant
Envoyez des en-têtes falsifiés directement sur votre port local, en contournant le tunnel :
curl -s http://127.0.0.1:3000/debug \\
-H 'X-Forwarded-For: 203.0.113.9' \\
-H 'X-Forwarded-Proto: https' \\
-H 'X-Forwarded-Host: evil.example'
Une configuration correcte rapporte l’adresse du socket comme client, HTTP en clair et votre hôte réel, car l’adresse de connexion n’était pas un proxy de confiance. Puis demandez le même itinéraire via le tunnel avec un X-Forwarded-For forgé et confirmez que votre application rapporte l’adresse ajoutée par le bord (edge), et non celle forgée. Effectuez les deux vérifications dans chaque environnement, car le staging hérite souvent d’un réglage de production écrit pour un proxy différent.
Faites travailler le bord avec les politiques de trafic
Les paramètres d’application sont votre ultime ligne de défense. Le bord voit la vraie connexion TCP, ce qui signifie que les règles qui s’y appliquent ne dépendent pas de la confiance d’un en-tête. Les politiques de trafic doivent être en amont de votre application pour trois missions :
- Règles d’en-têtes. Remplacez ou supprimez les en-têtes
X-Forwarded-*fournis par le client afin que l’application ne voie que les valeurs définies par le bord. - Règles IP. Autoriser ou bloquer les visiteurs selon leur adresse réelle de connexion, afin qu’une liste blanche IP bloque tout le trafic ou l’autorise entièrement.
- Limites de débit basées sur l’IP. Limitez chaque visiteur séparément, afin qu’un seul client bruyant n’épuise pas le quota de tout le monde, ce qui se produit exactement lorsque votre app s’appuie sur l’adresse de l’agent.
Les règles du bord et les réglages du framework se soutiennent mutuellement. Si jamais un trustProxy mal configuré passe, le bord a déjà normalisé les en-têtes et appliqué les limites à l’adresse réelle.
FAQ
Est-ce que X-Forwarded-For est sûr à utiliser pour la limitation de débit ? Seuls les éléments ajoutés par des proxys de confiance. Lisez l’en-tête à partir de la droite, en ignorant les proxys de confiance, et considérez la valeur la plus à gauche comme contrôlée par le visiteur.
Pourquoi est-ce que je obtiens une boucle de redirection uniquement via le tunnel ? Votre application voit du HTTP en clair car TLS se termine à l’edge. Configurez votre framework pour lire le schéma transmis, à partir d’un en-tête que le proxy définit de manière fiable.
Faut-il utiliser trust proxy: true ? Pas derrière un tunnel qui ajoute des en-têtes. Faites confiance à l’adresse de l’agent.
Les valeurs numériques de confiance sont-elles sûres ? Dans Fastify, elles ne l’étaient pas, jusqu’à ce que la version 5.12.1 les désactive. Préférez des Adresses explicites partout.
Est-ce que cela n’affecte que le développement local ? Non. Le même modèle de confiance s’applique en production derrière n’importe quel proxy ou équilibreur de charge.
Sources
- Fastify CVE-2026-3635 enregistrée et entrée GCVE
- Fastify CVE-2026-16732, Red Hat et trace Debian
- proxy-addr CVE-2026-90711 et Red Hat bug 2547485
- @fastify/proxy-addr CVE-2026-92395
- Express: derrière les proxys
- Fastify Request référence
- MDN: X-Forwarded-For
- ngrok HTTP endpoints et ngrok upstream headers
- Cloudflare en-têtes HTTP et cloudflared issue 1426
- Rails ActionDispatch::RemoteIp et ActionDispatch::SSL
- Avertissement Django SECURE_PROXY_SSL_HEADER, cité dans un fil DigitalOcean
- gunicorn docs déploiement et paramètres uvicorn
- django-ipware
Related InstaTunnel pages
Continue from this article into the most relevant product guides and workflows.
Related Topics
Keep building with InstaTunnel
Read the docs for implementation details or compare plans before you ship.