Proxys de confiance
Lorsqu'OxPHP s'exécute derrière un reverse proxy (Ingress Kubernetes, Cloudflare, AWS ALB, nginx), chaque requête arrive depuis l'adresse IP du proxy. Sans configuration des proxys de confiance, la limitation de débit, la journalisation des accès et $_SERVER['REMOTE_ADDR'] voient tous l'IP du proxy au lieu de la véritable IP client.
Configuration
# Comma-separated CIDR list
TRUSTED_PROXIES="10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
# Shorthand: all RFC-1918 + loopback + link-local (IPv4 and IPv6)
TRUSTED_PROXIES="private"
# Keywords and CIDRs combine in one list
TRUSTED_PROXIES="private,cloudflare,100.64.0.0/10"Chaque entrée séparée par des virgules est un CIDR, une adresse IP seule (un hôte unique) ou l'un des mots-clés private et cloudflare, quelle que soit la casse. Une entrée qui n'est rien de tout cela est une erreur au démarrage.
Lorsqu'elle n'est pas définie, OxPHP ignore tous les en-têtes de transfert. C'est la valeur par défaut sûre.
Fonctionnement
Lorsqu'une requête arrive depuis une IP de confiance, OxPHP inspecte les en-têtes de transfert par ordre de priorité :
Forwarded(RFC 7239) — l'en-tête standardiséX-Forwarded-For/X-Forwarded-Proto/X-Forwarded-Host/X-Forwarded-Port— le repli de fait
Si l'en-tête Forwarded est présent, les en-têtes X-Forwarded-* sont ignorés.
Extraction de l'IP client
OxPHP utilise l'algorithme rightmost-non-trusted (le premier non fiable en partant de la droite) — la même approche que celle employée par nginx (real_ip_recursive on), Caddy, Traefik et Apache :
X-Forwarded-For: 203.0.113.50, 172.16.1.1, 10.0.0.5
TCP peer: 10.0.0.1 (trusted)
Walk right-to-left:
10.0.0.5 → trusted → skip
172.16.1.1 → trusted → skip
203.0.113.50 → NOT trusted → client IPCela empêche l'usurpation via des valeurs ajoutées en tête de liste. Un attaquant peut ajouter de fausses IP à gauche, mais l'IP non fiable la plus à droite a été définie par le dernier proxy de confiance de la chaîne.
Une entrée peut porter un port : 203.0.113.50:4711 ou [2001:db8::1]:4711, la forme que certains répartiteurs de charge écrivent dans X-Forwarded-For (Azure Application Gateway, entre autres). Lorsque l'adresse client provient d'une telle entrée, son port devient REMOTE_PORT.
Le parcours s'arrête sur une entrée sans adresse ou qui ne s'analyse pas comme une adresse IP (un élément vide, unknown, un identifiant RFC 7239 obfusqué), ainsi que sur une ligne d'en-tête qu'OxPHP ne peut pas lire comme du texte (une ligne contenant un octet non ASCII, par exemple). Rien à gauche de ce point n'est utilisé, car cette partie de la chaîne peut n'être que ce que le client a envoyé. L'adresse client est l'entrée non fiable la plus à droite parmi celles situées à droite de ce point. Si toutes les entrées de cette partie sont de confiance, c'est la plus à gauche d'entre elles ; s'il n'y a aucune entrée à cet endroit, l'adresse n'est pas réécrite et reste celle du pair qui se connecte, port compris.
X-Forwarded-For: 198.51.100.9, unknown, 10.0.0.5
TCP peer: 10.0.0.1 (trusted)
Walk right-to-left:
10.0.0.5 → trusted → skip
unknown → does not parse → stop
198.51.100.9 → never reached
Client IP: 10.0.0.5 (every entry right of the stop is trusted → the leftmost of them)Avec le mot-clé cloudflare, une requête dont la chaîne de transfert montre qu'elle est arrivée depuis un nœud de bordure Cloudflare tire plutôt son adresse client de l'en-tête CF-Connecting-IP. Voir Derrière un CDN et un répartiteur de charge de plateforme.
Ce qui change
Lorsque TRUSTED_PROXIES est configuré et que l'IP de connexion est de confiance :
| Composant | Sans proxys de confiance | Avec proxys de confiance |
|---|---|---|
$_SERVER['REMOTE_ADDR'] |
IP du proxy | Véritable IP client ; l'IP du proxy lorsque les en-têtes de transfert ne fournissent aucune adresse |
$_SERVER['REMOTE_PORT'] |
Port source du proxy | Port client issu d'une entrée ip:port dans Forwarded ou X-Forwarded-For, sinon 0 ; le port du pair lorsque l'adresse n'est pas réécrite |
$_SERVER['HTTPS'] |
Selon la configuration TLS d'OxPHP | Issu de Forwarded: proto= ou X-Forwarded-Proto |
$_SERVER['REQUEST_SCHEME'] |
http ou https issu du TLS |
Issu du protocole transféré |
$_SERVER['SERVER_NAME'] |
Issu de l'en-tête Host |
Issu de Forwarded: host= ou X-Forwarded-Host |
$_SERVER['SERVER_PORT'] |
Issu de l'en-tête Host |
Issu de X-Forwarded-Port, sinon le port de X-Forwarded-Host / Forwarded: host=, sinon 443/80 |
| Limitation de débit | Par IP de proxy | Par IP client |
| Journal d'accès | IP du proxy | Véritable IP client |
Lorsque l'adresse client est réécrite, REMOTE_PORT est le port source du client si l'entrée dont provient l'adresse en porte un — un nœud RFC 7239 Forwarded: for="ip:port", ou une entrée ip:port dans X-Forwarded-For — et 0 sinon. Le port ne peut pas être reconstitué, il est donc mis à zéro plutôt que deviné. Une adresse tirée de CF-Connecting-IP n'a pas de port non plus, si bien que REMOTE_PORT vaut alors 0 également. Lorsque les en-têtes ne fournissent aucune adresse client et que l'adresse n'est pas réécrite, comme décrit sous Extraction de l'IP client, REMOTE_PORT reste le port du pair qui se connecte.
Réseaux private
Le raccourci private inclut :
| Réseau | Description |
|---|---|
10.0.0.0/8 |
Privé de classe A |
172.16.0.0/12 |
Privé de classe B |
192.168.0.0/16 |
Privé de classe C |
127.0.0.0/8 |
Loopback |
169.254.0.0/16 |
Link-local |
::1/128 |
Loopback IPv6 |
fc00::/7 |
Unique local IPv6 |
fe80::/10 |
Link-local IPv6 |
L'espace d'adressage partagé (100.64.0.0/10, RFC 6598) ne fait pas partie de private. Si votre répartiteur de charge se connecte depuis cet espace, ajoutez-le à côté du mot-clé : TRUSTED_PROXIES="private,100.64.0.0/10".
Réseaux cloudflare
Le mot-clé cloudflare ajoute les plages de bordure que Cloudflare publie sur cloudflare.com/ips : 15 plages IPv4 et 7 plages IPv6, telles que publiées le 2026-10-05. La liste est compilée dans OxPHP et ne change qu'avec une version d'OxPHP. Lorsque la liste de Cloudflare change la première :
- Une nouvelle plage. Ajoutez-la à côté du mot-clé :
TRUSTED_PROXIES="private,cloudflare,<new range>". Une plage listée compte comme l'un de vos propres proxys et non comme un nœud de bordure Cloudflare : les requêtes qui la traversent obtiennent donc leur adresse par la règle rightmost-non-trusted, et non depuisCF-Connecting-IP, jusqu'à ce qu'une version ajoute la plage au mot-clé. - Une plage retirée. Elle ne peut pas être soustraite du mot-clé. Pour l'écarter avant qu'une version d'OxPHP ne le fasse, listez sous forme de CIDR les plages auxquelles vous faites encore confiance au lieu d'utiliser le mot-clé. Vous renoncez alors aussi à la lecture de
CF-Connecting-IP, qui est liée au mot-clé.
Derrière un CDN et un répartiteur de charge de plateforme
Un déploiement courant place un CDN devant le répartiteur de charge d'une plateforme d'hébergement :
client → Cloudflare → load balancer → OxPHPAvec TRUSTED_PROXIES=private, seul le répartiteur de charge est de confiance. L'adresse non fiable la plus à droite dans X-Forwarded-For est alors le nœud de bordure Cloudflare qui s'est connecté au répartiteur de charge, et chaque requête semble provenir de Cloudflare — dans REMOTE_ADDR, le journal d'accès, la limitation de débit et l'attribut de span client.address. Faites confiance aux deux sauts :
TRUSTED_PROXIES="private,cloudflare"Si le répartiteur de charge se connecte depuis une adresse hors des plages private, listez aussi sa plage : TRUSTED_PROXIES="private,100.64.0.0/10,cloudflare".
Avec le mot-clé cloudflare, OxPHP détermine l'adresse client en trois étapes :
- 1Dépasser vos propres proxys. En partant du pair qui se connecte, OxPHP parcourt la chaîne de transfert vers la gauche (
Forwardeds'il est présent, sinonX-Forwarded-For), au-delà de vos propres proxys : les plagesprivateet les CIDR que vous avez listés. Une adresse située dans les plages de Cloudflare compte comme Cloudflare même si vous l'avez listée. - 2Chercher un saut Cloudflare. Si le premier saut qui n'est pas l'un de vos propres proxys est une adresse Cloudflare, la requête est passée par Cloudflare, et l'adresse client est l'en-tête
CF-Connecting-IP. L'en-tête doit apparaître exactement une fois et contenir une adresse IP valide. - 3Sinon, rightmost-non-trusted. Si la requête n'est pas passée par Cloudflare, ou si
CF-Connecting-IPest absent, répété ou mal formé, la règle rightmost-non-trusted s'applique, les plages de Cloudflare étant comptées comme de confiance.
Le saut où s'arrête le parcours est le pair qui se connecte ou une adresse enregistrée par l'un de vos propres proxys. Un client qui atteint directement le répartiteur de charge ne peut donc pas amener OxPHP à lire un CF-Connecting-IP qu'il a envoyé lui-même : le répartiteur de charge y enregistre l'adresse propre du client, et une adresse Cloudflare que le client a écrite plus à gauche n'est jamais atteinte. Comme la règle rightmost-non-trusted, cela suppose que vos proxys maintiennent l'en-tête que lit OxPHP — en ajoutant l'adresse qui s'est connectée à eux au lieu de transmettre telle quelle la valeur d'un client.
Vos proxys doivent aussi maintenir Forwarded. OxPHP le lit en priorité sur X-Forwarded-For, et Cloudflare le transmet tel que le visiteur l'a envoyé, puisqu'il ne fait pas partie des en-têtes que Cloudflare définit. Un répartiteur de charge qui ne maintient que X-Forwarded-For doit donc supprimer Forwarded de la requête. S'il laisse passer l'en-tête, n'importe quel client, via Cloudflare ou non, choisit l'adresse que voit OxPHP, avec ou sans le mot-clé cloudflare.
CF-Connecting-IP n'est pas lu non plus lorsque le parcours s'arrête avant d'atteindre une adresse Cloudflare — sur une entrée sans adresse ou qui ne s'analyse pas, ou sur une ligne qu'OxPHP ne peut pas lire comme du texte, comme décrit sous Extraction de l'IP client. Un enregistrement qu'un de vos proxys place sur une ligne à part n'est pas affecté par une ligne illisible envoyée auparavant par le client. Un enregistrement qu'un proxy écrit sur la même ligne qu'un texte envoyé par le client est perdu avec cette ligne : le parcours s'arrête là, et l'adresse provient de ce que vos proxys ont enregistré à droite de cette ligne, ou reste celle du pair qui se connecte — jamais de quoi que ce soit que le client a envoyé.
Cloudflare ajoute à X-Forwarded-For l'adresse de celui qui s'est connecté à lui, si bien que pour un visiteur ordinaire les deux en-têtes désignent le même client. Ils peuvent différer pour les requêtes que des Cloudflare Workers envoient à votre origine, qui partent elles aussi des adresses de Cloudflare, quel que soit le compte propriétaire du Worker. La référence des en-têtes HTTP de Cloudflare ne dit pas ce qu'une telle requête peut porter dans X-Forwarded-For. Pour CF-Connecting-IP, elle le dit : un Worker ne peut choisir la valeur que pour une requête vers un nom d'hôte de sa propre zone ; pour un nom d'hôte d'une autre zone Cloudflare, l'en-tête est l'adresse fixe 2a06:98c0:3600::103, et pour un nom d'hôte hors de Cloudflare, c'est l'adresse du client, que le Worker ne peut pas modifier.
Faire confiance aux plages de Cloudflare revient toujours à faire confiance à tout ce qui sort du réseau de Cloudflare, et pas seulement à votre propre zone. Si l'adresse client alimente une décision de sécurité, faites en sorte que l'origine n'accepte que le trafic de votre zone, par les moyens qu'offre votre plateforme.
Sécurité
- Valeur par défaut sûre — sans
TRUSTED_PROXIES, aucun en-tête de transfert n'est traité - Validation CIDR — des valeurs invalides dans
TRUSTED_PROXIESprovoquent une erreur au démarrage - Résistance à l'usurpation — l'algorithme rightmost-non-trusted ignore les valeurs ajoutées en tête par un attaquant
- Les requêtes provenant d'IP non fiables voient leurs en-têtes de transfert entièrement ignorés
Prompts pour votre agent
Ces prompts supposent un agent connecté au serveur MCP de la documentation OxPHP — il lira lui-même les pages dont il a besoin.
Configure les proxys de confiance pour mon déploiement OxPHP. Lis la page `security/trusted-proxies` via le serveur MCP de la documentation OxPHP.
Détermine à partir de mes fichiers d'infrastructure ce qui se trouve devant l'application, puis donne-moi la configuration exacte et dis-moi ce que chaque valeur admet et ce qu'elle laisserait un client falsifier si je me trompais.Voir aussi
- Limitation de débit — la limitation de débit par IP utilise l'IP client résolue
- Journalisation des accès — le champ
remote_ipindique l'IP client résolue - Référence de configuration — toutes les variables d'environnement