Tunnels multiplexés multi-locataires : Exécuter plus de 50 services locaux sur une seule connexion QUIC avec HTTP/3 Extended CONNECT
Découvrez comment faire fonctionner plus de 50 services locaux simultanément sur un seul port TCP en utilisant HTTP/3 Extended CONNECT avec des tunnels multiplexés multi-locataires.

Quick answer
Tunnels multiplexés multi-locataires : Exécuter 50+ services sur un seul: webhook testing answer
For local webhook testing, run your app locally, expose it with a public HTTPS tunnel, and paste the stable callback URL into the provider dashboard.
How do I test webhooks on localhost?
Start your local server, open a public HTTPS tunnel to that port, configure the provider webhook URL, and inspect events in your local logs.
Why does a stable webhook URL matter?
Stable URLs prevent provider dashboards from needing manual callback updates every time you restart a tunnel.
Exposer quelques services locaux à Internet est simple. En exposer des dizaines de manière fiable est plus difficile. Une machine de développement typique ou une passerelle en périphérie peut faire fonctionner des API REST, des endpoints gRPC, des serveurs WebSocket et des interfaces d’administration en même temps. Attribuer à chacun son propre port, sa propre connexion TCP ou son propre tunnel devient rapidement coûteux.
Cet article examine comment les tunnels modernes résolvent ce problème. Ils multiplexent de nombreux services sur une seule connexion sortante, et de plus en plus cette connexion est QUIC sur un seul port UDP (généralement 443). Il explique également ce que fait et ne fait pas HTTP/3 Extended CONNECT, car les détails comptent pour la conception du tunnel.
Le goulet d’étranglement : Un port ou une connexion par service
Trois problèmes apparaissent lorsque un tunnel évolue avec le nombre de services qu’il expose.
1. Surcharge par connexion
Si chaque service exposé obtient sa propre connexion TCP + TLS, l’agent en paie le prix de plusieurs façons :
- Coût de la poignée de main. Chaque connexion répète une poignée de main TCP et TLS.
- État du noyau et des descripteurs de fichiers. Chaque connexion maintient une socket, des buffers et une machine d’état TCP. Cela est particulièrement critique sur de petites passerelles et ordinateurs portables.
- État NAT et pare-feu. Chaque flux supplémentaire ajoute une entrée dans la table d’un dispositif à état, et les mappages NAT expirent éventuellement.
2. Prolifération des ports
Le transfert SSH classique (-L/-R) nécessite un port d’écoute distinct pour chaque service transféré. Cela implique plus de règles de pare-feu, une gestion accrue des ports, et plus de trafic sur des ports inhabituels souvent bloqués par des réseaux restrictifs.
3. Blocage head-of-line (HoL) TCP
Le multiplexage de nombreux flux logiques sur une seule connexion TCP, comme le fait HTTP/2, résout le nombre de connexions. Mais il crée un nouveau problème. TCP livre un flux de bytes ordonnés, donc un paquet perdu bloque tous les flux derrière jusqu’à sa retransmission. Un téléchargement en masse vers un service peut alors retarder un appel API sensible à la latence partageant le même tunnel.
Ce que font déjà les tunnels existants
La plupart des produits de tunnels matures multiplexent déjà de nombreux services sur une seule connexion plutôt que d’en ouvrir une par service :
- ngrok utilise un protocole de multiplexage de flux appelé muxado, décrit dans ses SDK open-source Rust et Go comme le protocole qui alimente les tunnels ngrok.
- Cloudflare Tunnel (
cloudflared) ouvre quatre connexions sortantes vers au moins deux centres de données Cloudflare, sur le port 7844. Son mode--protocol autopar défaut privilégie QUIC sur UDP et revient à HTTP/2 si les connexions UDP ne peuvent pas être établies. Le protocole legacyh2muxn’est plus supporté. Le transport QUIC decloudflaredest basé sur la bibliothèquequic-go.
L’idée de cet article, une connexion portant de nombreux services, est donc une pratique établie. La question ouverte concerne la couche de multiplexage sur laquelle construire et comment.
Pourquoi QUIC change la donne
QUIC (RFC 9000) fonctionne sur UDP et intègre TLS 1.3 (RFC 9001). Il possède des flux natifs, et la récupération de perte est suivie par flux. Lorsqu’un paquet contenant des données pour un flux est perdu, seul ce flux attend la retransmission. Les autres flux sur la même connexion continuent de livrer des données à l’application.
Deux précautions maintiennent la revendication habituelle de « zéro blocage head-of-line » honnête :
- L’ordre est toujours préservé dans un flux. Chaque flux est son propre flux de bytes ordonnés, donc une perte retarde toujours ce flux.
- La compression d’en-têtes HTTP/3 peut réintroduire un certain blocage. QPACK (RFC 9204) a été conçu pour réduire le HoL blocking par rapport à HPACK. Les références à sa table dynamique peuvent encore bloquer un flux si le décodeur n’a pas encore reçu la mise à jour de la table. Les implémentations limitent cela avec le paramètre
SETTINGS_QPACK_BLOCKED_STREAMS.
Pour les tunnels, QUIC offre d’autres avantages :
| Propriété | Pourquoi c’est important pour un tunnel |
|---|---|
| Une socket UDP, une connexion | Un seul 4-tuple pour que NAT puisse suivre, et une seule règle de pare-feu (UDP 443). |
| Récupération de perte par flux | Un transfert en masse bloqué ne fige pas les services non liés. |
| Handshake 1-RTT, reprise 0-RTT optionnelle | Reconnexion plus rapide. Attention avec le 0-RTT, car les données précoces peuvent être rejouées. |
| Identifiants de connexion et migration | Une connexion peut survivre à un changement de réseau client, comme Wi-Fi vers cellulaire, après validation du chemin (RFC 9000). |
| DPLPMTUD intégré | La taille des paquets peut augmenter lorsque le chemin le permet. |
Identifiants de connexion et migration
Les connexions QUIC sont identifiées par des Connection IDs, pas seulement par le 4-tuple IP/port. Un Connection ID a une longueur variable, jusqu’à 20 octets selon RFC 9000. Ce n’est pas une valeur fixe de 64 bits.
Lorsque le chemin réseau de l’agent change, le client peut sonder le nouveau chemin et migrer. Les extrémités effectuent d’abord une validation du chemin, donc la migration n’est pas instantanée ni automatique. Les applications et middleboxes peuvent aussi y interférer.
HTTP/3 Extended CONNECT : Qu’est-ce que c’est
HTTP CONNECT simple ouvre un tunnel TCP via un proxy. Extended CONNECT a été défini pour HTTP/2 dans RFC 8441 et ajoute un pseudo-en-tête :protocol, permettant à un seul flux CONNECT de transporter autre chose que du TCP brut. RFC 9220 l’amène à HTTP/3 en enregistrant le paramètre SETTINGS_ENABLE_CONNECT_PROTOCOL, valeur 0x08, identique à HTTP/2. Malgré son titre (“Bootstrapping WebSockets with HTTP/3”), le mécanisme est général. D’autres spécifications s’y appuient :
- WebSockets sur HTTP/3 (
:protocol = websocket), cas d’utilisation original. - CONNECT-UDP (RFC 9298, “Proxying UDP in HTTP”), qui tunnelise des datagrammes UDP.
- CONNECT-IP (RFC 9484, “Proxying IP in HTTP”), qui tunnelise des paquets IP.
- WebTransport sur HTTP/3, actuellement un brouillon IETF (
draft-ietf-webtrans-http3, révision 16 en juillet 2026), qui commence chaque session par une requête Extended CONNECT.
Une requête Extended CONNECT transporte ces pseudo-en-têtes :
| Pseudo-en-tête | Exemple | Notes |
|---|---|---|
:method |
CONNECT |
|
:protocol |
websocket, connect-udp, webtransport, ou un jeton privé comme tunnel-v1 |
Nomme le protocole utilisé sur le flux. tunnel-v1 est un exemple pour un design privé, pas une valeur enregistrée. |
:scheme |
https |
Requis pour Extended CONNECT. |
:authority |
service-50.tunnels.example.com |
Hôte cible, utilisé pour le routage du tunnel. |
:path |
/ |
Requis pour Extended CONNECT. |
Les deux côtés annoncent leur support en envoyant le paramètre dans leur cadre SETTINGS HTTP/3. Un client ne peut envoyer une requête Extended CONNECT qu’après réception du paramètre du serveur. quic-go, par exemple, vérifie cela avant d’envoyer.
Le problème de la direction : Qui ouvre le flux ?
Le point de conception le plus important, et celui qui est facile à mal faire, est qui peut ouvrir les flux.
Dans HTTP/3, les flux de requête sont initiés par le client, bidirectionnels QUIC. La première requête se fait sur le flux 0, puis 4, 8, 12, etc. RFC 9114 indique que HTTP/3 n’utilise pas de flux bidirectionnels initiés par le serveur, et qu’un client qui en reçoit doit le traiter comme une erreur de connexion de type H3_STREAM_CREATION_ERROR, sauf si une extension a été négociée.
Un tunnel complique cela. L’agent initie la connexion (il se trouve derrière NAT), donc l’agent est le client QUIC et HTTP/3. L’extrémité est le serveur. Mais les requêtes publiques arrivent à l’extrémité, qui doit les pousser vers l’agent. En HTTP/3 simple, l’extrémité ne peut pas ouvrir un flux de requête vers l’agent.
Les solutions possibles :
- Flux QUIC bruts avec un protocole applicatif personnalisé. QUIC lui-même permet à chaque extrémité d’ouvrir des flux bidirectionnels. L’extrémité ouvre un flux par requête publique et utilise son propre encodage. C’est une capacité générale de QUIC, pas spécifique à HTTP/3 Extended CONNECT. C’est la solution la plus simple pour les tunnels, et
cloudflaredconstruit son transport QUIC dessus. - Un pool de flux Extended CONNECT pré-ouverts. L’agent ouvre plusieurs flux Extended CONNECT inactifs vers l’extrémité, par exemple avec
:protocol = tunnel-v1. L’extrémité les réserve. Lorsqu’une requête publique arrive, l’extrémité en revendique un, écrit un petit préambule avec le nom du service cible, et relaye les octets. L’agent lit le préambule, se connecte au service local, copie dans les deux sens, et reconstitue le pool. Cela reste dans HTTP/3 standard, au prix d’une gestion du pool. - WebTransport sur HTTP/3. L’agent ouvre une requête Extended CONNECT avec
:protocol = webtransport. À l’intérieur, chaque extrémité peut ouvrir des flux, car le brouillon le définit par-dessus HTTP/3. Le brouillon est encore en brouillon Internet, donc susceptible de changer. WebTransport sur HTTP/3 ne supporte pas le 0-RTT. - Brouillons HTTP/3 inversés. Certains brouillons décrivent HTTP/3 avec les rôles inversés, et “HTTP/3 over WebTransport”, permettant à un serveur d’envoyer des requêtes à un client. Ce sont des brouillons, peu déployés.
Pour un tunnel, les options 1 ou 2 sont les choix pragmatiques aujourd’hui. L’option 3 est attractive si vous souhaitez une plomberie compatible navigateur et acceptez le statut de brouillon.
Flux avec pool de flux (option 2)
Client public Edge (serveur QUIC/H3) Agent (client QUIC/H3)
| | |
| |== poignée de main QUIC, UDP 443 =======|
| |== SETTINGS (ENABLE_CONNECT) =====|
| |== CONNECT :protocol=tunnel-v1 ===| flux 0 (réservé)
| |== CONNECT :protocol=tunnel-v1 ===| flux 4 (réservé)
| GET https://api.example.com | |
|------------------------------| |
| |-- revendique flux 0, envoie "api" ----|
| | |-- dial 127.0.0.1:8080
|---------- réponse ----------|-------- octets sur le flux 0 -------|
| |== agent ouvre un flux de remplacement =====| flux 8
Chaque requête publique correspond à un flux QUIC. Cinquante services, ou cinquante requêtes simultanées, partagent une seule socket UDP et une seule connexion QUIC.
Ce que signifie “50+” en pratique
Cinquante n’est pas une limite de protocole. C’est un nombre pratique qui se situe sous une valeur par défaut courante. Dans quic-go, quic.Config.MaxIncomingStreams par défaut autorise 100 flux bidirectionnels simultanés que le pair peut ouvrir. Si vous attendez plus de requêtes simultanées, augmentez-la délibérément du côté qui reçoit les flux.
La documentation de quic-go liste ces valeurs par défaut de contrôle de flux :
| Paramètre | Par défaut |
|---|---|
InitialStreamReceiveWindow |
512 Ko |
MaxStreamReceiveWindow |
6 Mo |
InitialConnectionReceiveWindow |
512 Ko |
MaxConnectionReceiveWindow |
15 Mo |
MaxIncomingStreams |
100 |
QUIC applique le contrôle de flux à deux niveaux : par flux et par connexion. La limite de flux empêche un service de consommer toute la mémoire tampon. La limite de connexion limite le total. La fenêtre de connexion doit être dimensionnée pour le nombre de flux que vous attendez en transfert actif, pas pour la somme de tous les maximums de flux. Si elle est trop petite, les flux se disputent le crédit de connexion et la bande passante diminue.
Ajuster l’hôte
Les implémentations QUIC fonctionnent en espace utilisateur sur UDP, donc les buffers UDP du noyau comptent. quic-go tente d’agrandir ses buffers socket mais ne peut dépasser rmem_max et wmem_max du noyau, qui sont faibles par défaut sous Linux. Sa recommandation :
sysctl -w net.core.rmem_max=7500000
sysctl -w net.core.wmem_max=7500000
Cela augmente les limites à environ 7,5 Mo. Persistant dans /etc/sysctl.d/ pour survivre à un reboot. Les outils Cloudflare indiquent aussi aux utilisateurs la même recommandation pour les buffers quic-go quand ils détectent des buffers faibles.
Contrôle de congestion
Ne supposez pas que passer à BBR est une simple configuration. La sysctl Linux tcp_congestion_control n’affecte que TCP, et le contrôle de congestion de QUIC est dans la bibliothèque QUIC. quic-go utilise CUBIC. Ses mainteneurs ont dit qu’ils ne prévoient pas d’implémenter BBR, bien que le support BBRv3 soit en suivi comme problème ouvert. Si BBR vous importe, vérifiez quels contrôleurs de congestion votre pile QUIC supporte.
Débit versus TCP
QUIC n’est pas toujours plus rapide. Le traitement en espace utilisateur coûte du CPU, et les noyaux classiques manquent de certains déchargements UDP que TCP possède. Sur des liens filaires rapides et peu perdus, HTTP/3 peut ne pas être plus rapide que HTTP/2 et consommer plus de CPU. QUIC est surtout utile sur des chemins à perte élevée, à latence importante ou mobiles. Mesurez sur votre propre réseau.
Comparaison technique
| Dimension | Transfert SSH | HTTP/2 sur TCP | HTTP/3 sur QUIC |
|---|---|---|---|
| Transport | TCP | TCP | UDP (QUIC) |
| Multiplexage | Canaux dans une connexion TCP, ou connexions séparées | Flux dans une connexion TCP | Flux QUIC natifs |
| La perte affecte | Tous les canaux sur cette connexion TCP | Tous les flux sur cette connexion TCP | Le flux affecté (plus dépendances de compression d’en-têtes) |
| Handshake | TCP + échange de clés SSH et authentification | TCP + TLS | Handshake combiné transport et TLS 1.3, 0-RTT en reprise |
| Changement réseau | La connexion se rompt | La connexion se rompt | Migration initiée par le client avec validation du chemin |
| Flux initiés par le serveur | Oui (via canaux) | Non (push déprécié en pratique, et Extended CONNECT est client-initié) | Possible en QUIC brut. Pas en HTTP/3 simple. Possible via WebTransport. |
| Accès pare-feu | TCP | TCP | Nécessite UDP, souvent bloqué |
Le problème : UDP n’est pas toujours disponible
Un tunnel QUIC n’est utile que si UDP passe. Les estimations varient, et les mesures anciennes sont datées. Les premières mesures de Google ont trouvé environ 3–5% de réseaux bloquant QUIC, et les réseaux d’entreprise sont un point sensible. Certains pare-feu d’entreprise bloquent ou limitent UDP/443 délibérément pour que les outils d’inspection voient le trafic TCP.
Les architectures de production doivent donc prévoir un repli TCP. cloudflared en mode auto le fait : il privilégie QUIC et revient à HTTP/2 si les connexions UDP ne peuvent pas être établies. Votre agent doit faire de même, et il doit indiquer quel transport est actif pour que les opérateurs comprennent pourquoi la performance varie.
N’oubliez pas qu’un flux UDP peut aussi être abandonné par des délais NAT. Envoyez des keepalives (frames PING de QUIC ou heartbeat applicatif) pour que les tunnels inactifs ne meurent pas silencieusement.
Sécurité et isolation multi-locataires
Multiplexer de nombreux services sur une seule connexion signifie qu’une seule stream compromise ou mal configurée ne doit pas atteindre les ressources d’un autre locataire.
- Authentifier la connexion, pas seulement les streams. Utilisez TLS mutuel ou un token porteur lors de la première connexion de l’agent. Gardez le processus d’enregistrement et d’authentification hors du 0-RTT, car les données précoces peuvent être rejouées.
- Liez l’agent aux noms d’hôte qu’il possède. L’extrémité ne doit router
tenant-a.example.comqu’à la connexion authentifiée comme tenant A. Ne faites jamais confiance à un nom d’hôte fourni par l’agent pour le routage sans le vérifier. - Utilisez une liste blanche dans l’agent. L’agent doit mapper les noms de service à des adresses locales spécifiques et rejeter tout le reste. En particulier, ne laissez jamais une requête choisir une destination arbitraire, ce qui pourrait permettre une attaque de type server-side request forgery vers des cibles comme l’endpoint de métadonnées cloud à
169.254.169.254. - Limiter la concurrence par locataire. Le mécanisme
MAX_STREAMSde QUIC limite le nombre de flux qu’un pair peut ouvrir. Ajoutez des limites de débit et des quotas en octets par locataire au niveau de l’extrémité pour éviter qu’une surcharge sur un nom d’hôte ne prive les autres. - Isoler les défaillances. Un reset sur un flux ne doit pas fermer la connexion. Réservez une fermeture au niveau de la connexion pour les violations de protocole.
Contexte d’adoption
HTTP/3 n’est plus exotique. W3Techs indique une utilisation par environ 38.6% des sites en mars 2026 et 39.7% en juillet 2026. Considérez ces chiffres comme “sites qui déclarent supporter”. Les mesures au niveau des requêtes sont plus faibles, et les données de Cloudflare Radar, résumées par une analyse tierce, placent HTTP/3 autour de 21% des requêtes mondiales au premier trimestre 2026. Pour les tunnels, le fait le plus important est que navigateurs, CDN et principales bibliothèques QUIC (quic-go, Google’s QUICHE, etc.) sont suffisamment matures pour s’appuyer dessus.
Conclusion
Exécuter plus de 50 services locaux sur une seule connexion est un problème résolu. Les tunnels matures multiplexent déjà de nombreux services sur un petit nombre de connexions, et QUIC ajoute la récupération de perte par flux, un port UDP unique et la migration de connexion côté client. Les points clés à bien faire :
- Les flux de requête HTTP/3 sont initiés par le client. Un tunnel inversé doit utiliser des flux QUIC bruts, un pool de flux Extended CONNECT pré-ouverts, ou WebTransport, pas un extrémité qui ouvre simplement des flux de requête vers l’agent.
- Extended CONNECT (RFC 8441, RFC 9220) est un mécanisme général. Il transporte WebSockets, UDP (RFC 9298), IP (RFC 9484) et WebTransport. Choisissez ou définissez le jeton
:protocoldélibérément. - Préparez-vous à UDP bloqué. Implémentez un repli TCP.
- Ajustez et mesurez. Augmentez les buffers UDP, dimensionnez les fenêtres de contrôle de flux pour la concurrence attendue, et testez avec HTTP/2 sur votre réseau avant de supposer que QUIC est supérieur.
Sources
- RFC 9220: Bootstrapping WebSockets with HTTP/3
- RFC 9298: Proxying UDP in HTTP
- RFC 9484: Proxying IP in HTTP
- RFC 9204: QPACK
- RFC 9000: QUIC transport
- Règles des flux HTTP/3 (RFC 9114 §6.1), discuté sur la liste IETF HTTP WG
- draft-ietf-webtrans-http3-16: WebTransport over HTTP/3
- Forward et Reverse HTTP/3 sur WebTransport (brouillon)
- Configuration Cloudflare Tunnel
- Paramètres d’exécution Tunnel Cloudflare
- Changelog de cloudflared (suppression de h2mux)
- OPNsense : buffers UDP et cloudflared
- muxado (le protocole de multiplexage derrière les tunnels ngrok)
- quic-go : tailles de buffers UDP
- quic-go : discussion BBR
- quic-go : documentation du package http3
- IETF : Applicabilité du protocole QUIC (blocage UDP et repli)
- W3Techs : utilisation de HTTP/3
- Analyse de l’adoption du protocole HTTP, T1 2026
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.