HTTP/3 et QUIC#
HTTP/3 représente une rupture profonde dans l’histoire des protocoles web. Alors que HTTP/1.1 et HTTP/2 s’appuient tous deux sur TCP comme couche de transport, HTTP/3 abandonne TCP au profit de QUIC, un protocole de transport nouvelle génération développé initialement par Google, puis standardisé par l’IETF (RFC 9000 pour QUIC, RFC 9114 pour HTTP/3). Cette transition répond à des limitations fondamentales que TCP impose à toutes les couches qui l’utilisent, limitations qui deviennent critiques à l’ère des réseaux mobiles, des connexions instables et des latences élevées.
Ce chapitre expose les problèmes architecturaux de HTTP/2 sur TCP, décrit en détail le fonctionnement de QUIC, explique comment HTTP/3 se greffe sur QUIC, et compare les performances à l’aide de visualisations et de code Python.
Les limites de HTTP/2 sur TCP#
HTTP/2 : une amélioration majeure mais incomplète#
HTTP/2 (RFC 7540, 2015) a apporté des avancées considérables par rapport à HTTP/1.1 : multiplexage des requêtes sur une seule connexion TCP, compression des en-têtes avec HPACK, push serveur, priorisation des flux. Sur papier, ces mécanismes éliminent les principaux goulots d’étranglement de HTTP/1.1 (files de requêtes, multiples connexions TCP, en-têtes répétitifs).
En pratique, HTTP/2 souffre d’un problème fondamental qu’il ne peut pas résoudre, car il est ancré dans la couche transport : le head-of-line blocking (HoL blocking) au niveau TCP.
Le head-of-line blocking au niveau TCP#
TCP garantit la livraison ordonnée et fiable des octets. Si un segment TCP est perdu, le récepteur ne peut pas livrer à l’application les segments suivants même s’ils sont déjà arrivés : il doit attendre la retransmission du segment manquant. Cette attente bloque tous les flux HTTP/2 multiplexés sur la connexion, même ceux qui n’ont aucun rapport avec le segment perdu.
Exemple concret de HoL blocking TCP
Imaginons trois requêtes HTTP/2 multiplexées sur une connexion TCP : images A, B, C. Leurs segments TCP sont entrelacés. Si un segment de l’image A est perdu, TCP bloque la livraison des segments des images B et C qui sont pourtant intacts en mémoire tampon. L’application attend la retransmission même si les données dont elle a besoin sont disponibles.
HTTP/2 résout le HoL blocking au niveau applicatif (plus besoin d’attendre qu’une requête HTTP/1.1 soit complète avant d’en envoyer une autre), mais introduit un HoL blocking au niveau transport qui n’existait pas de la même façon avec HTTP/1.1 (qui ouvrait plusieurs connexions TCP indépendantes).
La lenteur du handshake TCP+TLS#
Établir une connexion HTTPS nécessite deux handshakes successifs :
Handshake TCP : 1 RTT (SYN → SYN-ACK → ACK)
Handshake TLS 1.3 : 1 RTT (ClientHello → ServerHello+Certificate+Finished → Finished)
Total : 2 RTT avant que le premier octet de données puisse être transmis. Sur un réseau mobile avec 100 ms de RTT, cela représente 200 ms de délai incompressible avant toute donnée utile.
TLS 1.3 a introduit le 0-RTT resumption pour les sessions reprises, mais il reste limité (replay attacks, restrictions sur les requêtes non-idempotentes).
Ossification du protocole#
TCP est implémenté dans les systèmes d’exploitation (noyau Linux, Windows, macOS, iOS, Android). Toute modification de TCP nécessite des mises à jour de milliards d’équipements, un processus qui prend des années voire des décennies. De nombreux équipements réseau intermédiaires (pare-feux, NAT, middleboxes) inspectent ou modifient les paquets TCP, rendant toute extension risquée.
QUIC : architecture et principes fondamentaux#
QUIC sur UDP#
QUIC (Quick UDP Internet Connections) utilise UDP comme couche de transport. Ce choix peut sembler paradoxal — UDP est non fiable, sans ordre, sans contrôle de congestion — mais c’est précisément ce qui permet à QUIC d’implémenter ses propres mécanismes dans l’espace utilisateur, sans dépendre du noyau du système d’exploitation.
QUIC réimplémente au-dessus d’UDP :
La fiabilité et la retransmission des données
Le contrôle de flux (par flux et par connexion)
Le contrôle de congestion (compatible avec TCP : New Reno, CUBIC, BBR)
Le chiffrement intégral (TLS 1.3 est obligatoire, inclus dans QUIC lui-même)
Le fait qu’UDP soit présent partout (tout équipement réseau laisse passer UDP port 443) et que QUIC soit chiffré (les middleboxes ne peuvent pas l’inspecter) règle le problème d’ossification du protocole.
Streams indépendants et multiplexage sans HoL blocking#
La caractéristique centrale de QUIC est son modèle de streams indépendants. Une connexion QUIC peut contenir plusieurs streams (équivalents aux streams HTTP/2), mais chaque stream est livré indépendamment :
Les données d’un stream ne bloquent pas les autres streams en cas de perte de paquet
La retransmission ne concerne que le stream affecté par la perte
Les autres streams continuent d’être livrés à l’application immédiatement
HoL blocking HTTP/2 vs QUIC
HTTP/2 sur TCP : 3 streams, un paquet perdu → les 3 streams sont bloqués jusqu’à retransmission.
HTTP/3 sur QUIC : 3 streams, un paquet perdu → seul le stream concerné attend la retransmission. Les 2 autres streams continuent sans interruption.
Connexion QUIC et Connection ID#
Une connexion QUIC est identifiée par un Connection ID (CID), un identifiant opaque choisi par le client et/ou le serveur, et non par le tuple (IP source, port source, IP destination, port destination) comme TCP.
Cette propriété fondamentale permet la mobilité réseau : si un client passe du Wi-Fi à la 4G, son adresse IP change, mais le Connection ID reste valide. La connexion QUIC est maintenue sans interruption, sans avoir besoin de reestablir TLS. C’est particulièrement précieux pour les applications mobiles (vidéo en streaming, VoIP, navigation web sur smartphone).
Handshake QUIC : 1-RTT et 0-RTT#
Le handshake QUIC intègre directement TLS 1.3. Au lieu d’effectuer TCP puis TLS séparément, QUIC combine les deux en une seule négociation :
Première connexion (1-RTT) :
Le client envoie un paquet
Initialcontenant leClientHelloTLS.Le serveur répond avec
ServerHello, son certificat, et sonFinishedTLS.À partir de là, les deux parties peuvent échanger des données chiffrées.
Le client envoie son
Finishedet les premières données HTTP/3.
La négociation complète ne prend qu”1 RTT (contre 2 RTT pour TCP + TLS 1.3).
Reprise de connexion (0-RTT) :
Lors d’une connexion précédente, le serveur transmet un session ticket au client. Lors d’une reconnexion, le client peut envoyer des données applicatives dans le tout premier paquet, avant que le handshake soit terminé. Ces données 0-RTT peuvent être rejouées si le ticket est compromis, donc QUIC les limite aux requêtes idempotentes (GET).
Sécurité des données 0-RTT
Les données 0-RTT sont vulnérables aux attaques par rejeu (replay attacks). Un attaquant qui capture le premier paquet peut le renvoyer au serveur. C’est pourquoi les requêtes 0-RTT doivent être idempotentes (un GET répété ne doit pas avoir d’effet de bord). Les requêtes POST ou d’achat en ligne ne doivent jamais être envoyées en 0-RTT.
Connection ID et mobilité réseau#
Contrairement à TCP, dont la connexion est identifiée par le quintuplet (IP src, port src, IP dst, port dst, protocole), une connexion QUIC est identifiée par son Connection ID (CID), un nombre opaque d’octets inclus dans chaque paquet QUIC.
HTTP/3 : mapping sur QUIC#
Architecture de HTTP/3#
HTTP/3 (RFC 9114) utilise QUIC comme transport. L’architecture est radicalement différente de HTTP/2 sur TCP :
Couche |
HTTP/2 |
HTTP/3 |
|---|---|---|
Application |
HTTP/2 frames |
HTTP/3 frames |
Sécurité |
TLS 1.3 |
TLS 1.3 (dans QUIC) |
Transport |
TCP |
QUIC |
Réseau |
IP |
IP |
Liaison |
Ethernet/Wi-Fi |
Ethernet/Wi-Fi |
HTTP/3 conserve la sémantique de HTTP/2 (méthodes, codes de statut, en-têtes, corps) mais redéfinit le format des frames pour les adapter aux streams QUIC et introduit QPACK pour remplacer HPACK.
QPACK vs HPACK : compression des en-têtes#
HPACK (HTTP/2) comprime les en-têtes en utilisant une table d’encodage partagée entre client et serveur. Cette table est mise à jour de manière ordonnée sur la connexion TCP. Or, sur QUIC, les streams sont indépendants et les paquets peuvent arriver dans le désordre. Si le client envoie une mise à jour de la table HPACK sur le stream 1 et que la requête du stream 3 fait référence à une entrée de cette table, le stream 3 doit attendre que le stream 1 soit reçu — introduisant du HoL blocking.
QPACK (RFC 9204) résout ce problème :
Il utilise des streams de contrôle dédiés (encoder stream, decoder stream) séparés des streams de données.
L’encodeur peut envoyer des mises à jour de table sans bloquer les autres requêtes.
QPACK permet l’encodage sans blocage (en n’utilisant que des entrées de table statique ou en répétant les en-têtes sans référence à une entrée récente).
Table statique QPACK
QPACK définit une table statique de 99 entrées prédéfinies (paires (en-tête, valeur) fréquentes comme :status: 200, content-type: text/html, etc.). Cette table est identique côté client et serveur sans échange préalable, permettant une compression immédiate dès la première requête.
Frames HTTP/3#
HTTP/3 définit ses propres types de frames, transmises dans les streams QUIC :
Frame |
Description |
|---|---|
|
Corps de la requête ou de la réponse |
|
En-têtes compressés QPACK |
|
Annulation d’un server push |
|
Paramètres de connexion |
|
Annonce de server push |
|
Arrêt gracieux de la connexion |
|
Limite du push serveur |
HTTP/3 n’utilise pas les frames PRIORITY de HTTP/2 ; la priorisation est déléguée à QUIC (RFC 9218 pour les extensions de priorisation).
Adoption et déploiement#
État de l’adoption en 2025-2026#
HTTP/3 a été rapidement adopté par les grands acteurs du web :
Google : déploiement sur tous les services (Search, YouTube, Gmail) depuis 2020
Cloudflare : support HTTP/3 par défaut pour tous les sites derrière Cloudflare
Meta (Facebook/Instagram) : utilise QUIC en interne depuis 2017
Apple : support dans Safari depuis iOS 14/macOS 11
Selon les statistiques W3Techs, plus de 30 % des sites web actifs supportent HTTP/3 en 2025.
Alt-Svc et ALPN#
La découverte de HTTP/3 se fait en deux étapes :
Alt-Svc header : Le serveur annonce dans une réponse HTTP/1.1 ou HTTP/2 qu’il supporte HTTP/3 via l’en-tête Alt-Svc :
Alt-Svc: h3=":443"; ma=86400
Cela indique que le protocole h3 (HTTP/3) est disponible sur le port 443 et que cette information est valide 86400 secondes.
ALPN (Application-Layer Protocol Negotiation) : QUIC utilise l’extension TLS ALPN pour négocier le protocole applicatif. Le client propose h3 dans le ClientHello, le serveur confirme.
HTTPS Resource Records (HTTPS RR)
Le DNS propose désormais un enregistrement HTTPS (type 65) qui peut annoncer directement le support d’HTTP/3 sans nécessiter une première connexion HTTP/1.1 ou HTTP/2. Cela permet au client de tenter QUIC/HTTP/3 dès la première connexion, même sans visite précédente du site.
Code Python : requêtes HTTP/2 avec httpx et simulation de latence#
Utilisation de httpx pour HTTP/2#
La bibliothèque httpx supporte HTTP/2 (et HTTP/3 via une extension expérimentale). Voici comment effectuer des requêtes réelles avec HTTP/2 :
import httpx
import asyncio
import time
async def comparer_protocoles():
"""Compare les temps de réponse avec différentes configurations httpx."""
url = "https://www.cloudflare.com/"
# HTTP/1.1 (sans http2=True)
t0 = time.perf_counter()
async with httpx.AsyncClient(http2=False, verify=True) as client:
r1 = await client.get(url)
t1 = time.perf_counter()
print(f"HTTP/{r1.http_version} — {r1.status_code} — {(t1-t0)*1000:.1f} ms")
# HTTP/2
t0 = time.perf_counter()
async with httpx.AsyncClient(http2=True, verify=True) as client:
r2 = await client.get(url)
t1 = time.perf_counter()
print(f"HTTP/{r2.http_version} — {r2.status_code} — {(t1-t0)*1000:.1f} ms")
asyncio.run(comparer_protocoles())
Requêtes multiplexées HTTP/2#
import httpx
import asyncio
import time
async def requetes_multiplexees():
"""
Démontre le multiplexage HTTP/2 : N requêtes en parallèle
sur une seule connexion TCP.
"""
urls = [
"https://httpbin.org/delay/1",
"https://httpbin.org/delay/1",
"https://httpbin.org/delay/1",
"https://httpbin.org/delay/1",
]
# HTTP/2 : toutes les requêtes sur une seule connexion
async with httpx.AsyncClient(http2=True) as client:
t0 = time.perf_counter()
reponses = await asyncio.gather(*[client.get(url) for url in urls])
duree_h2 = time.perf_counter() - t0
print(f"HTTP/2 — {len(urls)} requêtes parallèles : {duree_h2:.2f}s")
# Sur une connexion rapide, ~1s au lieu de 4s car multiplexé
asyncio.run(requetes_multiplexees())
Résumé et perspectives#
HTTP/3 et QUIC constituent une avancée majeure dans l’architecture des protocoles web. En abandonnant TCP, ils résolvent des problèmes fondamentaux que HTTP/2 ne pouvait pas adresser : le HoL blocking au niveau transport, la lenteur des handshakes et la rigidité des connexions. Les bénéfices sont particulièrement visibles dans des conditions réseau dégradées (réseaux mobiles, Wi-Fi instable, longues distances).
Les points clés à retenir :
QUIC résout le HoL blocking TCP grâce à ses streams indépendants
1 RTT (et 0-RTT en reprise) au lieu de 2 RTT pour établir une connexion sécurisée
Le Connection ID permet la mobilité réseau transparente
QPACK adapte HPACK à l’environnement sans ordre de QUIC
L’adoption est rapide : +30 % des sites web en 2025, tous les grands CDN supportés
Les gains sont surtout visibles sur les réseaux dégradés et les accès mobiles