5 octobre 2025

Correction d'un bug et d'un correctif proposé dans NGINX HTTP/3 qui empêchait la valorisation de $http_host

Le patch Managed Server Srl est actuellement en cours d'examen pour être fusionné dans la branche principale officielle de NGINX.

Patch-NGINX-Http3-http_host

Au cours des derniers mois, le protocole HTTP / 3, basé sur QUIC, entre progressivement en production. Les principaux navigateurs le prennent en charge de manière stable, et de nombreux fournisseurs, dont Cloudflare, Google et Akamai, le considèrent désormais comme la norme de facto pour les connexions modernes. Nginx, à partir de la série 1.25, a introduit un module HTTP/3 expérimental, rendu plus stable plus tard avec la version 1.29.x.
Cependant, au cours de certaines sessions de tests approfondis menées par Serveur géré Srl, nous avons identifié un comportement anormal qui peut générer incohérences dans les réécritures, les journaux et les variables d'environnement, en particulier pour les sites et applications qui s'appuient sur la variable $http_host.

Le problème provenait d'une erreur de valorisation de cette variable lors du traitement des requêtes HTTP/3, c'est-à-dire des connexions QUIC. Contrairement à HTTP/1.1 et HTTP/2, où l'en-tête Host est automatiquement interprété et associé, dans le cas de HTTP/3 NGINX n'a ​​pas effectué l'initialisation correcte de la valeur correspondante lorsque le client a envoyé uniquement le pseudo-en-tête :authority.
Le résultat? $http_host est resté vide ou incompatible avec la valeur de l'autorité demandée, compromettant la compatibilité avec de nombreuses configurations et générant un comportement inattendu dans plusieurs scénarios d'application.

La variable $http_host et son importance

Pour bien comprendre l’ampleur du problème, il convient de rappeler que $http_host C'est l'une des variables les plus utilisées dans l'environnement NGINX.
Il est souvent utilisé dans :

  • blocs server e location avec une logique conditionnelle;
  • réécritures dynamiques basées sur l'hôte ;
  • règles de redirection vers des domaines canoniques ;
  • journalisation personnalisée;
  • proxy passe qui dépend de l'hôte de la requête d'origine.

Dans les configurations multi-domaines ou multi-locataires, l'absence ou l'incohérence de la valeur de $http_host Cela peut modifier le comportement de l'ensemble de la pile, obligeant un site à répondre avec le mauvais domaine ou empêchant les redirections HTTPS de fonctionner correctement.

La cause : le comportement de NGINX avec HTTP/3

Selon la RFC 9114, Section 4.2 (« Champs de pseudo-en-tête de demande »), chaque requête HTTP/3 doit contenir un champ :authority ou un en-tête Host.
Les deux ne peuvent pas être manquants et, s'ils sont présents, ils doivent contenir la même valeur.
Cependant, la plupart des clients HTTP/3 (tels que Chrome, Firefox et cURL dans --http3) envoyer uniquement le champ :authority, sans le dupliquer comme Host.

rfc9114-http3-pseudo-en-tête

NGINX, jusqu'à la version 1.29.1, ne transmettait pas automatiquement cette valeur dans l'en-tête interne correspondant Host, à partir de laquelle la variable est ensuite dérivée $http_host.
Cela a provoqué un écart fonctionnel entre HTTP/1.1 / HTTP/2 et HTTP/3 : dans les deux premiers cas $http_host il était toujours disponible, dans le troisième il ne l'était pas.

Le comportement, bien que conforme au cahier des charges, ce n'était pas cohérent avec la philosophie opérationnelle de NGINX, où les variables d'environnement doivent maintenir l'uniformité entre les protocoles, afin de ne pas forcer l'administrateur à différencier les configurations entre HTTP/2 et HTTP/3.

Le Patch : une intégration simple mais essentielle

Après une analyse minutieuse du code source de NGINX, et en s'inspirant d'un correctif similaire déjà présent dans ANGIE, le fork développé par Web Server LLC, nous avons créé un patch minimal mais efficace.
L'amendement, proposé par Marco Marcoaldi (CTO de Managed Server Srl), consiste en l'ajout d'une fonction dédiée :

ngx_http_v3_set_host(ngx_http_request_t *r, ngx_str_t *valeur)

Cette fonction s'occupe d'initialiser le champ r->headers_in.host lorsque le champ n'est pas présent mais existe :authority, en copiant sa valeur en toute sécurité.
Le code, intégré au fichier source src/http/v3/ngx_http_v3_request.c, utilise la structure de données interne de NGINX pour allouer un nouvel en-tête « hôte » cohérent avec les requêtes HTTP/3 entrantes.

diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c
index 5a6d4f9..6b8d77e 100644
--- a/src/http/v3/ngx_http_v3_request.c
+++ b/src/http/v3/ngx_http_v3_request.c
@@ -32,6 +33,8 @@
 
 static ngx_int_t ngx_http_v3_process_request(ngx_http_request_t *r);
 static ngx_int_t ngx_http_v3_parse_request_line(ngx_http_request_t *r);
+
+static ngx_int_t ngx_http_v3_set_host(ngx_http_request_t *r, ngx_str_t *value); // Enable $http_host
 
 static ngx_int_t ngx_http_v3_parse_request_headers(ngx_http_request_t *r);
 static ngx_int_t ngx_http_v3_parse_request_header(ngx_http_request_t *r);
@@ -1001,6 +1004,34 @@ ngx_http_v3_process_request(...)
 
+
+ngx_http_v3_set_host(ngx_http_request_t *r, ngx_str_t *value)
+{
+    ngx_table_elt_t  *h;
+
+    static ngx_str_t  host = ngx_string("host");
+
+    h = ngx_list_push(&r->headers_in.headers);
+    if (h == NULL) {
+        return NGX_ERROR;
+    }
+
+    h->hash = ngx_hash(ngx_hash(ngx_hash('h', 'o'), 's'), 't');
+
+    h->key.len = host.len;
+    h->key.data = host.data;
+
+    h->value.len = value->len;
+    h->value.data = value->data;
+
+    h->lowcase_key = host.data;
+
+    r->headers_in.host = h;
+    h->next = NULL;
+
+    return NGX_OK;
+}
+
+static ngx_int_t
 ngx_http_v3_parse_request_header(ngx_http_request_t *r)
 {
     ...
@@ -1038,7 +1068,8 @@ ngx_http_v3_parse_request_header(ngx_http_request_t *r)
-    if (r->headers_in.host && r->host_end) {
+    if (r->host_end) {
+        /* full :authority value (possibly with port) */
         ngx_str_t host;
         host.len = r->host_end - r->host_start;
         host.data = r->host_start;
@@ -1043,8 +1075,17 @@ ngx_http_v3_parse_request_header(ngx_http_request_t *r)
-        if (r->headers_in.host->value.len != host.len
-            || ngx_memcmp(r->headers_in.host->value.data, host.data, host.len)
-               != 0)
-        {
-            ngx_log_error(NGX_LOG_INFO, c->log, 0,
-                          "client sent \":authority\" and \"Host\" headers "
-                          "with different values");
-            goto failed;
-        }
+        if (r->headers_in.host) {
+            /* both Host and :authority present - ensure they are equal */
+            if (r->headers_in.host->value.len != host.len
+                || ngx_memcmp(r->headers_in.host->value.data,
+                              host.data, host.len) != 0)
+            {
+                ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                              "client sent \":authority\" and \"Host\" headers "
+                              "with different values");
+                goto failed;
+            }
+        } else {
+            /* Host is missing - set from :authority */
+            if (ngx_http_v3_set_host(r, &host) != NGX_OK) {
+                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+                return NGX_ERROR;
+            }
+        }
@@ -1491,7 +1532,6 @@ ngx_http_v3_finalize_request(ngx_http_request_t *r)
     ...
-
@@ -1730,6 +1772,7 @@ ngx_http_v3_run_request(ngx_http_request_t *r)
     ...
+

 

Le patch inclut également une vérification logique supplémentaire : si les deux champs Host e :authority sont présents et contiennent des valeurs différentes, un avertissement est enregistré dans le journal (NGX_LOG_INFO) et la demande est rejetée, comme l'exigent les spécifications RFC.
En pratique, le équivalence sémantique complète tra Host e :authority, éliminant toute ambiguïté possible.

Tests et validation en environnement réel

Une fois la modification terminée, le patch a été testé en trois phases :

  1. Environnement de développement isolé, pour vérifier la compilation et le comportement du formulaire http_v3.
  2. Environnement de mise en scène, avec des simulations de trafic HTTP/3 et des outils comme curl --http3, h2load, wrk e autocannon.
  3. Environnement de production sur un site Magento 2, en utilisant le build nginx-1.29.1 avec le drapeau --with-http_v3_module.

Dans tous les tests, la variable $http_host il a été correctement évalué avec la valeur dérivée du champ :authority, même en l'absence d'en-têtes Host.
Aucune régression ou impact sur les performances n'a été observé, même sous charge soutenue ou avec des connexions simultanées sur QUIC.
Les réponses HTTP ont maintenu la latence et le débit inchangés par rapport à la version d'origine.

Fusionner la proposition dans la branche principale officielle

Une fois le comportement consolidé et la compatibilité vérifiée, nous avons décidé de rendre le changement public.
Il a ensuite été exécuté fork du dépôt officiel NGINX sur GitHub et ouvert le Demande de tirage n° 917, visible ici :


HTTP/3 : initialiser l’en-tête Host depuis :authority pour activer la variable $http_host – Pull Request #917

Le PR est accompagné d'une description technique, de références à la RFC 9114, d'un code de diff complet et de notes sur les tests de validation de production.
Au même moment, l'accord a été signé Contrat de licence de contributeur F5 (CLA), qui est nécessaire pour permettre l'intégration officielle du changement dans le référentiel principal.

La demande d'extraction est actuellement en statut Ouvrez, en attente de révision par les mainteneurs de NGINX, principalement Maxime Dounin et l'équipe technique F5.
Conformément à la pratique courante, l'intégration n'aura lieu qu'après une révision manuelle et une vérification interne du code, mais les premiers retours de la communauté sont déjà positifs, car le correctif comble une lacune pratique que de nombreux administrateurs avaient signalée de manière informelle.

Contributions et philosophie Open Source

Chez Managed Server Srl, nous croyons fermement que l'open source n'est pas seulement une base technologique, mais un responsabilité partagée.
De nombreux problèmes que nous rencontrons quotidiennement dans la gestion d'un hébergement haute performance sont résolus grâce à de petits correctifs, optimisations ou correctifs qui, une fois partagés, deviennent des améliorations globales.
Redonner ces correctifs à la communauté signifie faire évoluer l’écosystème qui profite à nous tous.

Le correctif proposé n'introduit pas de nouvelles directives, ne modifie pas le comportement par défaut et n'a pas d'impact sur les performances ; il corrige simplement un problème. manque de logique dans le flux d'analyse d'en-tête HTTP/3, rendant le code plus cohérent et prévisible.

En attendant la fusion officielle

La demande d'extraction n° 917 reste ouverte et en cours d'examen pour le moment.
Conformément à la pratique courante, l’équipe NGINX effectuera une révision manuelle du code, vérifiant la compatibilité avec les autres composants principaux et le respect des normes de développement internes.
Une fois approuvé, le changement sera intégré dans le branche principale et ensuite publié dans la prochaine version stable.
Cela garantira que toutes les futures versions de NGINX, y compris celles distribuées par les principaux mainteneurs Linux, incluront nativement le correctif, sans avoir besoin de correctifs manuels.

conclusion

Le correctif de bogue lié à $http_host HTTP/3 représente un petit pas concret vers un NGINX plus solide, cohérent et compatible avec les nouveaux standards du Web.
L’intervention démontre comment même les Les entreprises italiennes peuvent contribuer activement à des projets de niveau mondial, participant non seulement en tant qu'utilisateurs, mais en tant qu'acteurs directs dans le développement de l'open source.

Pour ceux qui souhaitent en savoir plus ou tester le patch en attendant la fusion officielle, le code est disponible publiquement dans la pull request :

https://github.com/nginx/nginx/pull/917

En tant que Managed Server Srl, nous continuerons à suivre l'évolution du module NGINX HTTP/3 et à partager toute amélioration ou optimisation supplémentaire qui pourrait découler de notre travail quotidien sur l'hébergement haute performance, les systèmes Linux et les environnements Web complexes.
En attendant, ce patch représente une contribution concrète à la stabilité et à la prévisibilité de l’un des composants les plus critiques de l’infrastructure Internet moderne.

Vous avez des doutes ? Vous ne savez pas par où commencer ? Contactez-nous !

Nous avons toutes les réponses à vos questions pour vous aider à faire le bon choix.

Discute avec nous

Discutez directement avec notre support avant-vente.

0256569681

Contactez-nous par téléphone pendant les heures de bureau 9h30 - 19h30

Contactez-nous en ligne

Ouvrez une demande directement dans l'espace contact.

AVIS DE NON-RESPONSABILITÉ, Mentions légales et droits d'auteur. Red Hat, Inc. détient les droits sur Red Hat®, RHEL®, RedHat Linux® et CentOS® ; AlmaLinux™ est une marque commerciale de la AlmaLinux OS Foundation ; Rocky Linux® est une marque déposée de la Rocky Linux Foundation ; SUSE® est une marque déposée de SUSE LLC ; Canonical Ltd. détient les droits sur Ubuntu® ; Software in the Public Interest, Inc. détient les droits sur Debian® ; Linus Torvalds détient les droits sur Linux® ; FreeBSD® est une marque déposée de la Fondation FreeBSD ; NetBSD® est une marque déposée de la Fondation NetBSD ; OpenBSD® est une marque déposée de Theo de Raadt ; Oracle Corporation détient les droits sur Oracle®, MySQL®, MyRocks®, VirtualBox® et ZFS® ; Percona® est une marque déposée de Percona LLC ; MariaDB® est une marque déposée de MariaDB Corporation Ab ; PostgreSQL® est une marque déposée de PostgreSQL Global Development Group ; SQLite® est une marque déposée de Hipp, Wyrick & Company, Inc. ; KeyDB® est une marque déposée d'EQ Alpha Technology Ltd. ; Typesense® est une marque déposée de Typesense Inc. ; REDIS® est une marque déposée de Redis Labs Ltd ; F5 Networks, Inc. détient les droits sur NGINX® et NGINX Plus® ; Varnish® est une marque déposée de Varnish Software AB ; HAProxy® est une marque déposée de HAProxy Technologies LLC ; Traefik® est une marque déposée de Traefik Labs ; Envoy® est une marque déposée de CNCF ; Adobe Inc. détient les droits sur Magento® ; PrestaShop® est une marque déposée de PrestaShop SA ; OpenCart® est une marque déposée d'OpenCart Limited ; Automattic Inc. détient les droits sur WordPress®, WooCommerce® et JetPack® ; Open Source Matters, Inc. détient les droits sur Joomla® ; Dries Buytaert détient les droits sur Drupal® ; Shopify® est une marque déposée de Shopify Inc. ; BigCommerce® est une marque déposée de BigCommerce Pty. Ltd.; TYPO3® est une marque déposée de la TYPO3 Association; Ghost® est une marque déposée de la Ghost Foundation; Amazon Web Services, Inc. détient les droits sur AWS® et Amazon SES® ; Google LLC détient les droits sur Google Cloud™, Chrome™ et Google Kubernetes Engine™ ; Alibaba Cloud® est une marque déposée d'Alibaba Group Holding Limited ; DigitalOcean® est une marque déposée de DigitalOcean, LLC ; Linode® est une marque déposée de Linode, LLC ; Vultr® est une marque déposée de The Constant Company, LLC ; Akamai® est une marque déposée d'Akamai Technologies, Inc. ; Fastly® est une marque déposée de Fastly, Inc. ; Let's Encrypt® est une marque déposée d'Internet Security Research Group ; Microsoft Corporation détient les droits sur Microsoft®, Azure®, Windows®, Office® et Internet Explorer® ; Mozilla Foundation détient les droits sur Firefox® ; Apache® est une marque déposée de The Apache Software Foundation ; Apache Tomcat® est une marque déposée de The Apache Software Foundation ; PHP® est une marque déposée de PHP Group ; Docker® est une marque déposée de Docker, Inc. Kubernetes® est une marque déposée de The Linux Foundation ; OpenShift® est une marque déposée de Red Hat, Inc. ; Podman® est une marque déposée de Red Hat, Inc. ; Proxmox® est une marque déposée de Proxmox Server Solutions GmbH ; VMware® est une marque déposée de Broadcom Inc. ; CloudFlare® est une marque déposée de Cloudflare, Inc. ; NETSCOUT® est une marque déposée de NETSCOUT Systems Inc. ; ElasticSearch®, LogStash® et Kibana® sont des marques déposées d'Elastic NV ; Grafana® est une marque déposée de Grafana Labs ; Prometheus® est une marque déposée de The Linux Foundation ; Zabbix® est une marque déposée de Zabbix LLC ; Datadog® est une marque déposée de Datadog, Inc. ; Ceph® est une marque déposée de Red Hat, Inc. ; MinIO® est une marque déposée de MinIO, Inc. ; Mailgun® est une marque déposée de Mailgun Technologies, Inc. ; SendGrid® est une marque déposée de Twilio Inc. Postmark® est une marque déposée d'ActiveCampaign, LLC ; cPanel®, LLC détient les droits sur cPanel® ; Plesk® est une marque déposée de Plesk International GmbH ; Hetzner® est une marque déposée de Hetzner Online GmbH ; OVHcloud® est une marque déposée d'OVH Groupe SAS ; Terraform® est une marque déposée de HashiCorp, Inc. ; Ansible® est une marque déposée de Red Hat, Inc. ; cURL® est une marque déposée de Daniel Stenberg ; Facebook®, Inc. détient les droits sur Facebook®, Messenger® et Instagram®. Ce site n'est pas affilié, sponsorisé ou autrement associé à l'une des entités mentionnées ci-dessus et ne représente aucune de ces entités de quelque manière que ce soit. Tous les droits sur les marques et noms de produits mentionnés sont la propriété de leurs titulaires respectifs des droits d'auteur. Toutes les autres marques mentionnées sont la propriété de leurs titulaires respectifs. MANAGED SERVER® est une marque déposée européenne de MANAGED SERVER SRL, dont le siège social est situé Via Flavio Gioia, 6, 62012 Civitanova Marche (MC), Italie et le siège opérationnel Via Enzo Ferrari, 9, 62012 Civitanova Marche (MC), Italie.

JUSTE UN MOMENT !

Vous êtes-vous déjà demandé si votre hébergement était nul ?

Découvrez dès maintenant si votre hébergeur vous pénalise avec un site web lent digne des années 1990 ! Résultats immédiats.

Fermer le CTA
Retour en haut de page