Le carrefour, le concierge et l'accueil

À l'heure de pointe, un carrefour que personne ne règle se bouche en trois minutes. L'agent de circulation ne transporte rien et ne décide pas où vont les voitures. Il choisit qui passe maintenant, et par quelle voie.

Deux rues plus loin, un immeuble de bureaux a un concierge. Il regarde ce qui sort : les livreurs, le courrier, les employés qui descendent fumer. Il applique le règlement de l'immeuble, celui qui l'emploie.

En face, un hôtel a un comptoir d'accueil. Il regarde ce qui entre. Le visiteur ne connaît ni l'étage, ni le numéro de chambre, ni le nom de la gouvernante. Il parle à l'accueil, l'accueil oriente.

Ces trois personnages font le même geste : se placer entre deux parties. On les confond pour cette raison. Mais ils ne regardent pas dans la même direction, et surtout, ils ne sont pas payés par le même monde.

Le proxy direct est le concierge : installé par le réseau qui émet le trafic, il filtre ce qui sort. Le proxy inverse est l'accueil : installé par le service, il reçoit ce qui entre et masque ce qu'il y a derrière. Le répartiteur de charge est l'agent de circulation : il connaît plusieurs voies, sait laquelle est bouchée, et retire du plan celle qui vient de s'effondrer.

La question utile n'est jamais « proxy ou répartiteur ». Elle est : de quel côté du chemin cette boîte est-elle installée, et qui a décidé de l'installer ?
flowchart LR P1["Poste"] --> FP["Proxy direct
installé par le réseau émetteur"] P2["Poste"] --> FP FP --> NET["Internet"] NET --> LB["Proxy inverse et répartiteur
installés par le service"] LB --> S1["Instance 1"] LB --> S2["Instance 2"] LB --> S3["Instance 3"] classDef poste fill:#ede9e1,stroke:#8a4d16,color:#1f2428; classDef inter fill:#b5651d,stroke:#8a4d16,color:#f5f2ec; classDef backend fill:#d89253,stroke:#8a4d16,color:#2c3338; class P1,P2,NET poste; class FP,LB inter; class S1,S2,S3 backend;

Les trois boîtes peuvent tourner sur le même logiciel. C'est la position qui définit le rôle, pas le produit.


Le proxy direct

Le concierge travaille pour l'immeuble, pas pour le destinataire du colis. Il vérifie que ce qui sort a le droit de sortir, il tient un registre, et il refuse ce que le règlement interdit.

Un proxy direct est installé par l'organisation qui émet le trafic. Les postes le connaissent : variables HTTP_PROXY et HTTPS_PROXY, réglage système, fichier de configuration automatique. Certains réseaux l'imposent de façon transparente en détournant le routage, sans rien configurer sur les machines.

Conséquence immédiate : le serveur distant ne voit plus l'adresse du poste, il voit celle du proxy. Toute la sortie du réseau se présente sous une poignée d'adresses.

Ce qu'il apporte est concret. Un filtrage par catégorie ou par domaine, appliqué au même endroit pour tout le monde. Un contrôle d'accès nominatif, avec quota et journal. Un cache sortant qui mutualise ce que trois cents machines téléchargent en boucle, paquets système et images de conteneurs en tête. Et une traçabilité de ce qui quitte le réseau, ce qui intéresse autant l'exploitation que la sécurité.

Le prix est réel lui aussi.

Ce qu'il apporte Ce que ça coûte
Filtrage de contenu appliqué uniformément Un saut réseau de plus sur chaque requête sortante
Contrôle d'accès et quotas par utilisateur Une configuration à pousser et à maintenir sur chaque poste
Cache sortant mutualisé entre tous les postes Des applications qui ignorent les réglages de proxy
Journal centralisé du trafic sortant Un point de passage unique pour toute la sortie du réseau
Adresse source unique face au monde extérieur Sur TLS, il ne voit que l'hôte et le port

Ce dernier point mérite d'être posé nettement. Quand un client ouvre une connexion chiffrée à travers un proxy direct, il demande un tunnel avec la méthode CONNECT définie par la RFC 9110. Le proxy relaie des octets opaques : il connaît le nom d'hôte demandé, pas le contenu. Pour inspecter le contenu, il faut intercepter la connexion et déployer une autorité de certification privée sur toutes les machines. Ce n'est plus un réglage réseau, c'est une décision de sécurité, traitée sous cet angle dans Pare-feu, WAF, proxy, IDS/IPS, VPN.

Un proxy direct protège le réseau qui l'héberge, jamais le service distant. Il est financé, configuré et exploité par ceux qui émettent les requêtes.

Le proxy inverse

L'accueil de l'hôtel travaille pour l'hôtel. Le visiteur ne sait pas combien d'étages sont ouverts, quelles chambres sont en travaux, ni où loge la direction. Il obtient une réponse, et rien d'autre.

Un proxy inverse est installé par le propriétaire du service. Le DNS pointe sur lui, le client croit parler au serveur d'origine, et il n'a aucune raison de soupçonner qu'il y a autre chose derrière.

Ce que ça déplace tient en cinq points.

La terminaison TLS d'abord. Les certificats vivent au même endroit, se renouvellent au même endroit, et les instances applicatives parlent en clair sur un réseau privé ou dans un second tunnel maîtrisé. Une seule chaîne à surveiller au lieu de vingt.

Le routage applicatif ensuite. Un hôte vers un service, un préfixe d'URL vers un autre, une version d'API vers une troisième. Le découpage interne devient invisible de l'extérieur, ce qui permet de le changer sans prévenir personne.

Le cache et la compression, qui déchargent l'application de ce qu'elle recalcule inutilement. C'est le sujet du billet suivant, Cache distribué et CDN, qui traite les patterns d'accès et l'invalidation.

Le masquage de la topologie. Les instances portent des adresses privées, ne sont jointes que par le proxy, et n'ont pas besoin d'exister dans le DNS public. La surface exposée se réduit à une seule machine.

Le point d'entrée unique, enfin, où l'on pose ce qui doit s'appliquer à tout le monde : authentification en bordure, limitation de débit, pare-feu applicatif, normalisation des en-têtes.

Un détail se retourne souvent contre les équipes. Puisque le proxy inverse ouvre lui-même la connexion vers l'instance, l'application voit l'adresse du proxy comme adresse cliente. Il faut donc transmettre l'adresse réelle explicitement, via X-Forwarded-For ou l'en-tête Forwarded de la RFC 7239. Et il faut n'accorder confiance à cet en-tête que lorsqu'il vient de son propre proxy : un client peut l'écrire lui-même, et une limitation de débit qui compte sur un en-tête falsifiable ne limite rien du tout.

Proxy direct Proxy inverse
Installé par Le réseau qui émet le trafic Le propriétaire du service
Placé Au plus près du client Au plus près du serveur
Connu du client Oui, il est configuré pour lui Non, le client croit parler à l'origine
Ce qu'il voit du client Son identité, son poste, son utilisateur Une adresse IP, plus ce qu'on lui transmet
Ce qu'il cache Le client au serveur distant La topologie interne au client
Ce qu'il protège Le réseau émetteur Le service et ses instances
Dans La Ville Le concierge qui filtre ce qui sort L'accueil qui reçoit ce qui entre
Les deux boîtes portent le même nom parce qu'elles font le même geste. Elles n'ont ni le même propriétaire, ni le même bénéficiaire, ni le même angle mort.

Le répartiteur de charge

L'agent de circulation ne se contente pas de laisser passer. Il sait qu'une voie est barrée, il sait laquelle vient de rouvrir, et il redistribue le flot en conséquence.

C'est exactement ce qu'un répartiteur ajoute à un proxy inverse : la connaissance de plusieurs destinations, et un avis sur leur état. Un proxy inverse qui ne connaît qu'une seule instance derrière lui n'est pas un répartiteur, c'est un intermédiaire.

En pratique, nginx, HAProxy, Envoy ou Traefik tiennent les deux rôles. La distinction porte sur une fonction que l'on active ou non, pas sur une famille de produits.

Un répartiteur apporte quatre choses qu'un simple relais n'a pas. Il teste la santé des instances au lieu de leur faire confiance. Il retire automatiquement celle qui ne répond plus, et la réinsère quand elle revient. Il draine les connexions avant un retrait volontaire, pour qu'un déploiement ne coupe pas les requêtes en cours. Et il rend possible le remplacement d'une instance sans que personne s'en aperçoive.

Il devient du même coup le point de passage unique de tout le trafic entrant. On le double donc systématiquement, adresse virtuelle partagée entre deux machines, annonce anycast ou service géré redondé. Le sujet est traité en entier dans Haute disponibilité : redondance, réplication, bascule.

Reste la question de la couche à laquelle il travaille.

Répartition L4 Répartition L7
Ce qu'il lit Adresses IP, ports, état de la connexion TCP La requête applicative complète
TLS Traverse sans être déchiffré Terminé, donc contenu lisible
Routage possible Par port et par pool de destination Par hôte, chemin, en-tête, cookie
Reprise sur erreur Impossible, il ne sait pas ce qu'est une requête Possible sur les requêtes rejouables
Cache et compression Hors de portée Disponibles
Coût processeur Faible, très haut débit Élevé, proportionnel au trafic
Cas d'usage Bordure de réseau, protocoles non HTTP Devant un service, routage fin

Beaucoup d'infrastructures empilent les deux étages. Un répartiteur L4 en bordure encaisse le volume brut et distribue vers une flotte de répartiteurs L7, qui font ensuite le travail fin devant chaque service. Le premier étage est bête et rapide, le second est intelligent et coûteux.

Un répartiteur L4 déplace des paquets, un répartiteur L7 comprend des requêtes. Tout ce que le L7 sait faire de plus se paie en processeur et en accès au contenu déchiffré.

Les algorithmes de répartition

L'agent ne fait pas passer les voitures une par voie, chacune son tour, sans regarder où elles vont. Une voie mène à un parking souterrain qui met deux minutes à avaler un véhicule, l'autre à un boulevard dégagé.

Les algorithmes de répartition ne se valent pas, et le choix par défaut du produit installé n'est presque jamais discuté.

Le tour de rôle distribue en rotation. Simple, prévisible, sans état à maintenir. Il suppose deux choses fausses en général : que les instances ont la même capacité, et que les requêtes coûtent la même chose. Une requête d'export lourde et un appel de santé comptent pour le même poids dans son calcul.

Le tour de rôle pondéré corrige la première hypothèse. On attribue un poids proportionnel à la capacité. Avec trois instances de poids 4, 2 et 1, le total vaut 4 + 2 + 1 = 7, et sur sept requêtes consécutives la première en reçoit quatre, la deuxième deux, la troisième une. Soit 4/7 ≈ 57 %, 2/7 ≈ 29 % et 1/7 ≈ 14 % du trafic. Le défaut n'est pas dans la formule : c'est que les poids sont fixés le jour de l'installation et jamais revus après le renouvellement du parc.

Le moindre nombre de connexions envoie la requête à l'instance qui en traite le moins à cet instant. Il s'adapte tout seul aux requêtes de durées très inégales, ce qui en fait souvent le meilleur choix par défaut. Deux pièges le guettent : une instance qui vient de démarrer affiche zéro connexion et reçoit tout le trafic d'un coup, et sous HTTP/2 une seule connexion transporte des dizaines de flux, ce qui rend le compteur peu représentatif.

Le moindre temps de réponse combine les connexions actives et la latence mesurée. Plus fin, mais il a un mode de défaillance vicieux : une instance qui échoue immédiatement répond très vite, paraît la plus rapide, et attire donc le trafic qu'elle est justement en train de perdre. On ne l'utilise qu'avec un contrôle de santé sérieux à côté.

Le hachage d'adresse IP calcule une empreinte de l'adresse cliente et en déduit toujours la même instance. Ce n'est plus de l'équilibrage, c'est de l'affinité déguisée en équilibrage.

Algorithme Principe Quand c'est le bon choix Ce qui casse
Tour de rôle Rotation stricte Instances identiques, requêtes homogènes Requêtes de coûts très inégaux
Tour de rôle pondéré Rotation proportionnelle au poids Parc hétérogène, machines de générations différentes Poids fixés une fois et jamais revus
Moindre connexions Vers l'instance la moins chargée Durées de traitement variables Instance neuve inondée, multiplexage HTTP/2
Moindre temps de réponse Latence mesurée plus charge Recherche de latence de queue L'instance qui échoue vite paraît la plus rapide
Hachage d'IP Empreinte de l'adresse cliente Besoin d'affinité sans cookie NAT, adresses mobiles, changement de taille du pool

La session collante

Le hachage d'IP existe pour une seule raison : garder un utilisateur sur l'instance qui détient son état de session. Il échoue de trois façons.

Derrière une passerelle d'entreprise ou une traduction d'adresse d'opérateur, des milliers de clients partagent une adresse et atterrissent tous sur la même instance. Un téléphone qui bascule du wifi au réseau mobile change d'adresse et perd sa session en pleine transaction. Et le jour où l'on ajoute une machine, le modulo change : une large fraction des clients est réaffectée d'un coup. Le hachage cohérent règle ce troisième point, pas les deux premiers.

L'affinité par cookie est plus précise, mais elle laisse le problème intact : l'état vit dans une instance. Quand celle-ci tombe, ses utilisateurs perdent leur session, et aucun algorithme ne rattrape ça.

Le hachage d'IP est une rustine. La vraie réponse est de sortir l'état de l'instance, ce que pose Couplage lâche, stateless et cache. Un service sans état rend le choix de l'algorithme presque indolore.

Le contrôle de santé

Un agent de circulation qui n'a pas vu l'effondrement continue d'envoyer les voitures dans le trou. Le répartiteur a le même défaut : il ne sait rien tant qu'il n'a pas regardé.

Deux mécanismes existent, et ils se complètent au lieu de se remplacer.

Le contrôle actif interroge périodiquement une adresse dédiée et attend un code de retour donné, dans un délai fixé. Il détecte une instance morte même quand plus aucun trafic ne lui est envoyé, ce qui permet de la réinsérer proprement.

Le contrôle passif observe le trafic réel. Après un nombre d'échecs consécutifs, l'instance est éjectée pour une durée donnée. Il ne coûte aucune requête supplémentaire et repère ce qu'une sonde trop simple laisse passer.

Contrôle actif Contrôle passif
Source du signal Requêtes de sonde dédiées Erreurs du trafic réel
Détecte une instance sans trafic Oui Non
Détecte une panne partielle Seulement si la sonde la teste Oui, par nature
Coût Une requête par instance et par intervalle Nul
Permet la réinsertion Oui Après expiration d'un délai

Côté nginx en version libre, le contrôle est passif : il se règle sur chaque entrée server de l'upstream.

upstream app {
    least_conn;
    server 10.0.1.11:8080 max_fails=3 fail_timeout=30s;
    server 10.0.1.12:8080 max_fails=3 fail_timeout=30s;
    server 10.0.1.13:8080 max_fails=3 fail_timeout=30s;
}

server {
    listen 443 ssl;
    server_name app.exemple.fr;
    # certificats omis

    location / {
        proxy_pass http://app;
        proxy_set_header Host              $host;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

HAProxy fournit le contrôle actif en standard, avec un intervalle et deux seuils.

backend app
    balance leastconn
    option httpchk GET /healthz
    http-check expect status 200
    server app1 10.0.1.11:8080 check inter 2s fall 3 rise 2
    server app2 10.0.1.12:8080 check inter 2s fall 3 rise 2
    server app3 10.0.1.13:8080 check inter 2s fall 3 rise 2
sequenceDiagram participant LB as Répartiteur participant A as Instance A participant B as Instance B LB->>B: sonde toutes les 2 s B-->>LB: 200 Note over B: la dépendance ne répond plus LB->>B: sonde 1 B--xLB: délai dépassé LB->>B: sonde 2 B--xLB: délai dépassé LB->>B: sonde 3 B--xLB: délai dépassé Note over LB,B: retrait de B après 3 échecs consécutifs LB->>A: tout le trafic vers les instances saines

Ces trois paramètres décident combien de requêtes tombent dans le trou. Posons le calcul.

Le service encaisse 1 200 requêtes par seconde réparties sur 4 instances, soit 1 200 / 4 = 300 requêtes par seconde par instance. Une instance meurt juste après une sonde réussie, cas le plus défavorable. Avec inter 2s fall 3, il faut attendre la sonde suivante à +2 s, puis celle à +4 s, puis celle à +6 s pour atteindre le troisième échec. Le retrait intervient donc 6 secondes après la panne. Pendant ce temps : 300 × 6 = 1 800 requêtes envoyées vers une machine morte.

Resserrer en inter 1s fall 2 ramène la détection à 1 + 1 = 2 secondes, soit 300 × 2 = 600 requêtes perdues, trois fois moins. En échange, on double le nombre de sondes et on augmente le risque de retirer une instance saine sur un simple pic de latence. Le contrôle passif comble l'intervalle : à 300 requêtes par seconde, trois requêtes se présentent en une dizaine de millisecondes, et l'instance est éjectée dès qu'elles échouent. Le délai réel dépend alors du mode de panne, immédiat si la connexion est refusée, égal à trois délais d'attente si la machine se contente de ne plus répondre.

Reste à décider ce que la sonde teste vraiment. Une adresse qui renvoie 200 sans rien vérifier ne prouve que la présence d'un processus. Une sonde qui interroge la base de données prouve davantage, mais le jour où la base hoquette, les quatre instances échouent en même temps et le répartiteur vide le pool entier. Il faut distinguer deux questions : le processus est-il vivant, et est-il en état de prendre du trafic maintenant ?

Un contrôle de santé mal réglé transforme un incident local en panne totale. La sonde qui teste les dépendances partagées supprime toute la flotte au premier tremblement de la base.

Scénario B : Cloudflare, le 2 juillet 2019

Un intermédiaire mutualisé concentre le trafic de milliers de clients. C'est son intérêt, et c'est aussi son risque, documenté avec une précision rare par Cloudflare dans son rapport public sur la panne du 2 juillet 2019.

Ce jour-là à 13h42 UTC, l'entreprise déploie une mise à jour de son jeu de règles de pare-feu applicatif géré. L'une des règles contient une expression régulière dont un fragment, .*(?:.*=.*), provoque un retour arrière catastrophique sur certaines entrées : le temps d'évaluation explose au lieu de rester linéaire.

Le déploiement est global et immédiat. Cloudflare l'explique dans son rapport : les règles de pare-feu applicatif servent à contrer des menaces en cours, elles sont donc poussées vite et partout, sans le déploiement progressif appliqué au reste de son logiciel.

En quelques secondes, le processeur sature sur les machines qui traitent le trafic HTTP, sur l'ensemble du réseau mondial. Les visiteurs des sites servis par Cloudflare reçoivent des erreurs 502. Cloudflare chiffre à 82 % la chute du trafic au plus fort de l'incident, dans sa communication publiée le jour même ; le rapport détaillé, écrit quelques jours plus tard, cite 80 % de trafic perdu au moment du diagnostic. Il écarte l'hypothèse d'une attaque et rappelle que l'entreprise n'avait pas connu de panne globale depuis six ans.

L'équipe déclare l'incident au plus haut niveau de gravité, puis désactive globalement le pare-feu applicatif. Le trafic revient à la normale à 14h09, soit environ vingt-sept minutes après le déploiement de la règle.

Les mesures annoncées ensuite par Cloudflare visent le mécanisme plutôt que la règle fautive : passer les règles de pare-feu applicatif en déploiement progressif, tout en gardant la possibilité d'une poussée globale immédiate pour une attaque en cours ; ajouter un profilage de performance de chaque règle au jeu de tests ; rétablir la protection contre la consommation excessive de processeur, supprimée par erreur lors d'un remaniement, et passer à un moteur d'expressions régulières offrant des garanties de temps d'exécution.

La redondance ne protège pas d'un défaut partagé par toutes les machines. Une flotte de proxys inverses parfaitement redondée tombe d'un bloc si la même configuration fautive y est poussée en même temps.

L'ancrage dans La Ville est direct. On installe un agent à chaque carrefour pour que la circulation continue quand l'un d'eux s'absente. Mais si les cent agents reçoivent la même consigne erronée par radio à la même seconde, les cent carrefours se bloquent ensemble. Ce qui compte alors n'est plus le nombre d'agents, c'est le délai pour annuler la consigne.


Points clés

  • La position définit le rôle, pas le produit. Le proxy direct est installé par le réseau émetteur et filtre ce qui sort ; le proxy inverse est installé par le service et reçoit ce qui entre. Le même logiciel peut tenir les deux rôles.
  • Un répartiteur de charge est un proxy inverse qui a un avis sur l'état de ses destinations. Contrôle de santé, retrait automatique, réinsertion et drainage : c'est là qu'est la valeur ajoutée, pas dans la distribution elle-même.
  • L4 déplace des paquets, L7 comprend des requêtes. Le routage fin, la reprise sur erreur, le cache et la compression n'existent qu'au prix de la terminaison TLS et du processeur qui va avec.
  • Le moindre nombre de connexions est un meilleur défaut que le tour de rôle dès que les requêtes ont des durées inégales. Le hachage d'IP n'est pas un algorithme d'équilibrage, c'est une affinité qui casse sur le NAT, le réseau mobile et l'ajout d'une machine.
  • Les paramètres du contrôle de santé se calculent. Avec un intervalle de 2 secondes et trois échecs avant retrait, une instance morte reçoit du trafic pendant 6 secondes, soit 1 800 requêtes à 300 requêtes par seconde.

Dans la série

Palier 2 : Faire circuler. Domaine : Trafic.


Pour aller plus loin