15 - Proxy : forward, reverse et les headers qui trahissent
Ce que tu vas apprendre
- La différence entre forward proxy et reverse proxy
- Les headers X-Forwarded-For, X-Real-IP et Via
- Comment configurer Caddy ou Nginx en reverse proxy
- Le Proxy Protocol et pourquoi il existe
Prerequisites
- 14 - Redirections
- Avoir deja utilise un serveur web (Nginx, Caddy, Apache)
Le mot "proxy" revient tout le temps. Dans les docs, dans les configs, dans les conversations entre devs. Et pourtant, je croise régulièrement des gens qui confondent forward proxy et reverse proxy. Ou qui ne savent pas pourquoi leur appli voit toujours 127.0.0.1 comme IP client. On va demystifier tout ca.
Forward proxy : le proxy cote client
Un forward proxy se place entre le client et Internet. C'est le client qui sait qu'il passe par un proxy. Le serveur de destination, lui, ne voit que l'IP du proxy.
Client --> Forward Proxy --> Internet --> Serveur
Cas d'usage classiques :
- Proxy d'entreprise : ta boîte filtre le trafic sortant. Tu ne peux pas aller sur Twitter pendant les heures de bureau (en theorie).
- Anonymisation : le serveur ne voit pas ton IP réelle. C'est le principe de base d'un VPN grand public.
- Cache partage : Squid en entreprise qui met en cache les telechargements pour éviter de pomper la bande passante.
Le navigateur ou le système d'exploitation est configure explicitement pour utiliser le proxy. La requête HTTP ressemble a ca :
httpGET http://example.com/page HTTP/1.1
Host: example.com
Note que l'URL dans la ligne de requête est absolue. C'est comme ca que le proxy sait ou envoyer la requête.
Reverse proxy : le proxy cote serveur
Un reverse proxy se place devant tes serveurs. Le client ne sait meme pas qu'il existe. Il pense parler directement a ton appli, mais en réalité il parle a Nginx, Caddy, HAProxy ou un load balancer cloud.
Client --> Reverse Proxy --> Serveur(s) applicatif(s)
Cas d'usage :
- Load balancing : repartir le trafic entre plusieurs instances de ton appli
- Terminaison SSL : le reverse proxy gere le certificat TLS, ton appli recoit du HTTP en clair en interne
- Compression et cache : gzip/brotli applique au niveau du proxy
- Protection : rate limiting, WAF, filtrage avant que la requête touche ton code
Si tu deploies une appli en production, tu as un reverse proxy. Meme si tu ne le sais pas. Vercel, Cloudflare, AWS ALB : ce sont tous des reverse proxies.
Les headers qui racontent l'histoire
Quand une requête passe par un proxy, l'IP source change. Ton appli ne voit plus le vrai client. C'est la que les headers proxy entrent en jeu.
X-Forwarded-For
Le grand classique. Chaque proxy ajoute l'IP du hop précédent :
httpX-Forwarded-For: 203.0.113.50, 70.41.3.18, 150.172.238.178
La première IP est (en theorie) le client original. Les suivantes sont les proxies intermediaires. Je dis "en theorie" parce que n'importe qui peut forger ce header. Ne fais jamais confiance a X-Forwarded-For sans savoir combien de proxies tu as devant toi.
X-Real-IP
Plus simple : une seule IP, celle du client original. Souvent positionne par le premier reverse proxy de la chaîne. Nginx l'utilise beaucoup.
httpX-Real-IP: 203.0.113.50
Via
Le header Via est défini dans la spec HTTP. Il trace la chaîne de proxies :
httpVia: 1.1 proxy1.example.com, 1.1 proxy2.example.com
Le format est version pseudo. En pratique, je le vois moins souvent que X-Forwarded-For, mais il a le merite d'etre standardise.
Forwarded (le standard moderne)
La RFC 7239 a introduit un header propre pour remplacer tout le bazar X- :
httpForwarded: for=203.0.113.50;proto=https;by=proxy1.example.com
L'adoption est lente. La plupart des outils utilisent encore les X-Forwarded-*.
Caddy en reverse proxy
Caddy est mon choix par défaut pour un reverse proxy. La config est ridiculement simple :
app.example.com {
reverse_proxy localhost:3000
}
C'est tout. Caddy gere automatiquement le certificat Let's Encrypt, le renouvellement, la terminaison TLS, les headers X-Forwarded-For et X-Forwarded-Proto. Sur paltemps.fr, c'est exactement cette config (ou presque) qui tourne.
Pour du load balancing :
app.example.com {
reverse_proxy localhost:3000 localhost:3001 localhost:3002 {
lb_policy round_robin
health_uri /health
health_interval 10s
}
}
Nginx en reverse proxy
Nginx demande plus de configuration, mais offre un contrôle plus fin :
nginxserver {
listen 443 ssl;
server_name app.example.com;
ssl_certificate /etc/ssl/cert.pem;
ssl_certificate_key /etc/ssl/key.pem;
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;
}
}
Si tu oublies les proxy_set_header, ton appli verra 127.0.0.1 comme IP client et http comme protocole. Un classique.
Le Proxy Protocol
Les headers HTTP c'est bien, mais ils ont un problème : ils fonctionnent uniquement pour du HTTP. Si tu proxifies du TCP brut (une base de donnees, du SMTP, du WebSocket sans upgrade HTTP), tu n'as pas de headers.
Le Proxy Protocol (invente par HAProxy) resout ca. C'est une ligne de texte ajoutee au début de la connexion TCP :
PROXY TCP4 203.0.113.50 10.0.0.1 56324 443
Version 2 utilise un format binaire plus efficace. Nginx, HAProxy, Caddy, AWS NLB supportent tous le Proxy Protocol. Mais attention : le serveur de destination doit le supporter aussi, sinon il va interpréter cette ligne comme des donnees corrompues.
Le piège de la confiance
Un point que je vois mal gere partout : la confiance dans les headers proxy.
Si ton appli fait du rate limiting base sur X-Forwarded-For, un attaquant peut simplement envoyer :
httpX-Forwarded-For: 1.2.3.4
Et changer d'IP a chaque requête. La solution : configure ton reverse proxy pour ecraser le header X-Forwarded-For entrant et ne garder que ce qu'il voit lui-meme. Ou mieux, utilise le Proxy Protocol.
Résumé
- Un forward proxy est cote client (cache, filtrage, anonymisation)
- Un reverse proxy est cote serveur (load balancing, SSL, protection)
X-Forwarded-Fordonne la chaîne d'IPs, mais ne lui fais pas confiance aveuglémentX-Real-IPdonne une seule IP, posee par le premier proxy- Caddy gere tout automatiquement, Nginx demande de la config manuelle
- Le Proxy Protocol fonctionne au niveau TCP, pas HTTP
Article précédent : 14 - Redirections Article suivant : 16 - HTTP/2