30 September 2025

Managed Server Srl fixes the annoying bug of missing $http_host in HTTP/3 on ANGIE and then maybe NGINX

From our direct experience with a production e-commerce site, through official RFCs and GitHub repositories, we tell how we identified, documented and submitted the proposal that prevented the correct use of $http_host over HTTP/3 with NGINX and ANGIE.

ANGIE-VS-NGINX

Introduction

Anyone who works with web servers on a daily basis knows that Details make the differenceA small missing variable, undocumented behavior, or an oversight in a protocol implementation can render features that, on paper, should improve performance and security unusable.

we at Managed Server Srl we came across one of these concrete cases, working on a real project, an e-commerce based on Magento 2. A seemingly marginal problem – the absence of $http_host in HTTP/3 connections – it turned out to be a real obstacle to the adoption of the protocol, so much so that it blocks the native activation of HTTP/3 on the site.

The path that led us to identify, document and finally fix this bug It was a tortuous process: we first tried with the official NGINX team (without success), then we decided to contact them directly ANGIE, the Russian fork developed by former NGINX engineers after the crisis with F5 Networks.

The result? An accepted and already integrated patch in ANGIE's code (commit 140c3a6), which fixes an annoying but very impactful bug once and for all.

This is the detailed account of that story.

Context: HTTP/3 and NGINX

HTTP/3 is not just “the latest version of the HTTP protocol” that follows the progressive numbering of previous specifications. It is, in effect, a paradigm shift in the way browsers and servers communicate on the Internet.

To understand its scope, we need to remember how we used to work with previous versions:

  • With HTTP / 1.1TCP connections were persistent, but each request brought significant overhead. The result was often slow loading of asset-rich pages (images, CSS, JS).
  • With HTTP / 2, officially introduced in 2015, a huge step forward has been made thanks to the multiplexing on the same TCP channel: multiple requests could coexist without having to open separate connections. However, TCP remained the Achilles heel, especially in cases of packet loss. A single missing packet was enough to block the entire flow until its retransmission: the so-called head-of-line blocking.

With HTTP/3 the perspective changes completely:

  • TCP is abandoned in favor of HERE C, a protocol built on top of UDP that directly integrates functionality retransmission, congestion control and cryptographic security.
  • Latency is reduced dramatically, especially in the handshake: With QUIC, TLS negotiation is integrated into the protocol itself, eliminating unnecessary round-trips.
  • The problem of head-of-line blocking disappears: each flow travels independently, without a lost packet blocking the entire connection.

QUIC VS TCP

It is no coincidence that big players like Cloudflare, Google, Meta, Akamai And many CDNs have pushed hard for the adoption of HTTP/3. A significant portion of the world's traffic already travels over this protocol, even though end users often aren't aware of it.

From a systems perspective, enabling HTTP/3 in a modern web server like NGINX would seem like a trivial operation: you compile the version with QUIC and HTTP/3 support (now released by default in all rpm and deb packages of the latest NGINX versions), add a few configuration directives and, theoretically, you're done. In reality, in some rare cases, the difference between manual and production is abysmalThis is where the subtleties, the missing variables, and the incompatibilities with complex applications like CMS or e-commerce come into play.

The real case: bselfie.it on Magento 2 — when $http_host it makes the difference

bselfie-homepage

At the beginning of 2024 we inherited the system management of bselfie.it, an e-commerce of technical cosmetics based on Magento 2The transition occurred from an architecture Amazon AWS to our optimized infrastructure, with a cost cuts of approximately 90% e significantly higher performance.
The cost reduction was possible because we eliminated several redundant layers (managed services “as a service”, excess instances and non-essential components) and consolidated on NVMe dedicated server with OpenZFS storage and stack NGINX + PHP-FPM + Varnish Cache + MariaDB/Redis + ElasticSearch Optimized for Magento 2.

However, we weren't able to add the icing on the cake by enabling HTTP/3. Whenever we did it the "classic" way, the German and English multilingual stores automatically stopped working.

In our specific case, the platform was NGINX 1.28.3, with an installation of Magento 2 which he hosted three multilingual sites: Italian, English and German.

To manage the Magento multi-store, we adopted an atypical configuration (inherited from the old company that followed them) based on the directive map, which uses the value of $http_host to determine the execution code (MAGE_RUN_CODE) correct.

In practice, the logic was this:

map $http_host $MAGE_RUN_CODE {
www.bselfie.it website_it;
www.bselfie.it/en website_en;
www.bselfie.de website_de;
}

Why this setup worked perfectly with HTTP/2

With HTTP / 2, the variable $http_host it is correctly valued starting from the header Host.
Then:

  • A request to www.bselfie.it activated the Italian store.
  • A request to www.bselfie.it/en activated the English store.
  • A request to www.bselfie.de activated the German store.

La mapping based on $http_host It was reliable, consistent, and ensured that each language/market received its correct configuration.

What happens with HTTP/3

With the enablement of HTTP / 3, the scenario changes dramatically.
This protocol no longer uses the header directly Host, but the higher pseudoheader :authority.
The problem is that, in our setup, $http_host is not automatically populated by :authority.

Result:

  • $http_host remains empty in HTTP/3.
  • The map no longer has a valid input value.
  • All requests end up falling into the default store (in this case the one Italian).

Practical example:

  • A request to www.bselfie.it/en no longer activated website_en, because $http_host it was empty.
  • Magento interpreted the request as if it were directed to the main store, effectively breaking the multi-store logic.

In other words: routing was shutting down and the entire multilingual infrastructure collapsed on a single store. This wasn't acceptable, and between having a site with broken multilingual support and a perfectly functioning site with HTTP/3 disabled, we opted for this option since the TTFB remained good even with only HTTP72.

Speedvitals-Bselfie

Why $host it was not a viable solution

A possible technical objection could be: “Why not replace $http_host $host? ".
It's true: in the world NGINX/ANGIE this has now become the standard practice.
When migrating an existing configuration from HTTP/2 to HTTP/3, it is almost always recommended to replace $http_host $host, because this last variable is populated correctly even with HTTP/3.

In most cases this “workaround” works without problems:

  • $host contains the domain requested by the client,
  • It is managed directly by the NGINX core,
  • and avoids finding yourself with an empty variable as happens instead with $http_host in HTTP/3.

However, in our specific case — a Multilingual and multi-store Magento 2 like bselfie.it$host was not an adequate replacementHere are the main reasons.

1) $host contains only the domain

  • $host exclusively enhances the domain name, for example www.bselfie.it.
  • It does not take into account additional parts such as /en o /de, which in our configuration were essential to correctly determine the store to serve.
  • Result: all requests would fall on the same domain, losing linguistic distinction and collapsing multi-store routing.

2) Unwanted fallback

  • If the header Host is missing or invalid, $host falls on the first server_name of the configuration block or even on the server IP.
  • In a multilingual Magento context, this means directing legitimate requests to the wrong store, with unpredictable consequences on user flows and conversions.

3) Inconsistency with existing configuration

  • The entire Magento stack (and many third-party modules) expect the variable HTTP_HOST match exactly the host provided by the client.
  • Altering this behavior by forcing $host in place of $http_host leads to Unwanted redirects, infinite loops, or inconsistently generated URLs.

For most websites and web applications, in short, replacing $http_host $host It's a quick trick, used practically always to make HTTP/3 work with existing NGINX configurations.
But in the case of bselfie.it and other complex scenarios with multi-store or multi-language routing, $host it's just too “normalized” and poor in information to handle the load of application logic that Magento requires.

Consequently, it was essential that $http_host would also be populated correctly in HTTP/3, drawing on the pseudoheader :authority.

The proposed fix

From the analysis we have done on the real case of bselfie.it, the conclusion was immediate:
NGINX (and forks like ANGIE) should populate $http_host even in HTTP/3, taking it directly from the pseudohader value :authority.

We are not talking about a revolutionary change, but simply about maintain cross-protocol consistency:

  • in HTTP / 1.1 e HTTP / 2, $http_host is correctly populated by the header Host;
  • in HTTP / 3, Where Host no longer exists and is replaced by :authority, it's natural that $http_host must inherit this value.

In this way:

  • applications that depend on $http_host (like Magento 2 in our case) would continue to work without any changes;
  • It would not be necessary to manually intervene on the configuration with workarounds such as:
fastcgi_param HTTP_HOST $host;

which, as we explained, works in most scenarios but not in advanced and multistore setups.

It seemed to us the most logical and peaceful thing: don't break what already works in HTTP/2, simply by ensuring that in HTTP/3 $http_host be filled by :authority.

Essential chronology of requests

  • January 16, 2025 — Let's open the report on Nginx explaining that in HTTP/3 $http_host remains empty because it is not initialized by :authority, proposing to align the behavior with HTTP/1.1 and HTTP/2.
    Issue: “Support for populating $http_host with the :authority header in HTTP/3 (QUIC)” (NGINX #455).
  • January 23, 2025 — We are re-proposing the same request on ANGIE (NGINX drop-in fork).
    Issue: “Support for populating $http_host with the :authority header in HTTP/3 (QUIC)” (ANGIE #109).
  • August 12, 2025ANGIE accepts and integrates the patch: “HTTP/3: initialize 'Host' header from authority.” The commit (SHA 140c3a6) initializes correctly Host/$http_host starting from :authority when the field Host it is not present, aligning with RFC 9114.
  • September 29, 2025 — Valentin V. Bartenev “VBart” the ANGIE maintainer from Web Server LLC in Moscow, informs us that our request has been closed with the implementation being communicated to us and subsequent distribution in the next ANGIE release.

It is curious that, even in ANGIE, the first response from the maintainer - which arrived the day after we opened our request - was use $host in place of $http_host, effectively rejecting the proposal. A suggestion that, although it is a common practice in migrations to HTTP/3, in our context it would never have held up in production neither on a functional level nor in terms of coherence with existing configurations. Then, with a more careful analysis conducted in August, the position has changed: it has been recognized that initialize $http_host da :authority it is the correct choice and, more than an exception, it represents the rule to ensure continuity between HTTP/2 and HTTP/3 without breaking application stacks.

http_host-http3-quic-nginx-angie

From the specifications point of view, the RFC 9114 (HTTP/3) section 4.3.1 “Request Pseudo-Header Fields” clarifies that clients must use :authority in place of Host and, when both are present, must contain the same valueThe implementation in ANGIE therefore makes the behavior of HTTP/3 consistent with that of HTTP/2 even on the practical level of server variables.

rfc9114-http3-pseudo-header

including the relevant translation into Italian:

If the pseudo-header field :scheme identifies a scheme that requires an authority component (including "http" e "https"), the request MUST contain or the pseudo-header field :authority or the header Host. If these fields are present, THEY MUST NOT be empty. If both fields are present, THEY MUST contain the same value. If the schema does not include a mandatory authority component and none is provided in the request target, the request HE MUST NOT contain pseudo-header fields :authority or the header Host.

An HTTP request that omits required pseudo-header fields or contains invalid values ​​for those pseudo-header fields is malformed.

HTTP/3 does not define a way to carry the version identifier included in the HTTP/1.1 request line. HTTP/3 requests implicitly have a protocol version of "3.0".

Because for us this solution was peaceful and "logically perfect"

It is worth clarifying why, from our operational and architectural point of view, this choice was obviousThe goal was to preserve the expected behavior of applications when moving from HTTP/2 to HTTP/3, avoiding fragile workarounds and ensuring continuity without affecting proven configurations. In other words: maximum compatibility, minimum friction, full standards alignment.

  • Cross-protocol consistency: in HTTP/1.1 and HTTP/2 $http_host is always valued; in HTTP/3 the semantic equivalent is :authority. Popular $http_host da :authority avoid regressions on application stacks that expect HTTP_HOST (Magento multi-store first and foremost).
  • Zero interventions on existing configurations: there is no need to introduce or spread the workaround with $host, which in complex cases can lead to inconsistencies.
  • Explicit adherence to specifications: the hookup to :authority This is precisely what the RFC indicates as a client-side authoritative source; the resulting server-side implementation is natural.

Furthermore, it didn't mean undertaking a titanic undertaking, but simply populating an empty variable with the contents of another variable, if present. Nothing more, nothing less.

Why NGINX first and then ANGIE?

Our strategy was clear from the beginning: Get the feature validated and introduced by at least one of the two projects, NGINX or ANGIE, thus creating a strong technical precedent which, within a a few days or at most a few months, also pushed the other party to align itself simply technical inertia and consistency with the specifications (RFC 9114).

F5 Networks Russia

This line of action fits into a non-trivial historical and geopolitical framework: NGINX was born in Russia in the early 2000s (led by Igor Sysoev), it grows to become a pillar of the modern web and is then acquired by F5 Networks; with the outbreak of the war between Russia and Ukraine, the picture gets more complicated, F5 closes its Russian branch and several historical engineers – left without a place – they founded ANGIE in Russia, a drop-in replacement compatible with the syntax and philosophy of NGINX, technically very responsive. In this context, from European company, we are held to a strict compliance: between sanctions, regulatory constraints, country risk management and internal policies, we cannot adopt ANGIE in production, as far as we recognize its technical value.

This is why we proceeded like this: first NGINX, which is the web server we use every day and the natural recipient of our proposal; then ANGIE, to increase the likelihood that the solution would be analyzed and implemented quickly, creating that "precedence effect" capable of dragging the other project along as well. In essence, we wanted one of the two implemented it first the mapping of :authority su $http_host in HTTP/3, avoiding application regressions and fragile workarounds: if ANGIE had done it (as it actually happened), NGINX would have had a great incentive to converge; if NGINX had done it, ANGIE would have followed For compatibility and its very ambition to be a drop-in replacement. A simple, concrete, and realistic strategy: put the solution into production where there is greater integration speed, and let the rest of the ecosystem realign accordingly.

We remember that ANGIE is a drop-in replacement for NGINX: in practice it means that it can replace NGINX with no (or minimal) changes to configuration, commands and operational flows. syntax of nginx.conf, directives (server, location, map, upstream, etc.), the layout of the virtual host, command line flags and the behavior of the principals modules (http/stream/mail) They are kept compatible, so you can replace the binary and continue using the same files and procedures.

ANGIE's code is largely derivative/compatible with that of NGINX, so the patch that initializes $http_host da :authority it is conceptually portable even on NGINX with minimal diff (subject to differences in tree, build system, dynamic modules and licensing policies). In other words, The fix introduced in ANGIE is technically backportable to NGINX, and this is precisely the value of a true drop-in: immediate operational compatibility e ease of re-use of work between the two projects.

Conclusions and considerations

In closing, what continues to leave us perplexed is like a request simple, well documented and perfectly aligned with the RFC was welcomed with little interest da NGINX (F5 Networks), while - even after an initial invitation to "fall back on $host” — was instead recognized as valid and implemented by former Russian NGINX developers in the fork ANGIEFrom our operational point of view, the proposal was pure common sense: popular $http_host da :authority in HTTP/3 to ensure continuity with HTTP/1.1/HTTP/2 and avoid regressions on production applications. It's a small improvement but high impact, especially in complex ecosystems like Magento multi-store.

Then there is a broader, almost "cultural" plan. It is difficult not to notice that the original soul of NGINX — the one made of pragmatism and attention to real production — seems to have remained In Russia, in the now closed branch from which the group that gave life to ANGIE. Geopolitical limitations permitting, in a desirable future of peace we would be very happy to move our loads to ANGIE: the facts show that the team was able to listen, evaluate and ground a targeted and standard-compliant correction, confirming — with a pinch of irony — that “the Russians do it better”It's not about cheering: it's about evaluation. technique of responsiveness and quality of implementation.

NGINX-http_host-http3

On the NGINX front, there has been a further step on our part responsibility towards the ecosystem: we have updated the proposal originally opened to January 2024, highlighting that ANGIE has already integrated the fix and explicitly recalling the RFC 9114The message is clear: we are not asking for a revolution, but cross-protocol consistency and protection of real deployments. We hope that this time the request will not be rejected. underarm grip and that we will see it in production in the next 1.29.2 by the end of October 2025, at the latest by the end of 2025It would be an important signal: for those who develop in upstream, for those who maintain production and, above all, for those who — like us — work every day to make the web more fast, reliable and consistent from standards to the code that serves pages to users, sometimes even – as in this case – getting their hands dirty – unlike our “colleagues” Sunday hosting sellers – who limit themselves to writing posts on how to “Install a WordPress plugin”, or how to create a mailbox on Plesk or cPanel.

Pathetic.

Patch release for NGINX and merge request in the Master Branch.

Five days after the release of the initial patch, and taking advantage of a few free hours on a quiet Sunday in October, in Managed Server Srl we decided to take a further step forward: bring the modification developed in the ANGIE context directly to NGINX, the main open source project.
The goal was to give back to the community an improvement that has proven useful and stable in our test and production environments, while ensuring full compliance with the latest technical standards, in particular the RFC 9114 (HTTP/3).

The initiative was led by Marco Marcoaldi, as CTO of Managed Server Srl, who personally followed the entire adaptation, validation, and official proposal process. The decision to intervene arose after having verified, in several real-world managed hosting contexts, an anomalous behavior of the NGINX HTTP/3 module: the variable $http_host it was not valued correctly in the presence of the pseudo-header field :authority.
A technical detail, certainly, but with important implications for those who use complex rewrites, HTTP header-based logging rules, or conditional routing logic.

The first step was to thoroughly analyze the differences between ANGIE, the fork of NGINX developed by Web Server LLC, and the main release maintained by the official NGINX team (now under the aegis of F5). The patch was already present in ANGIE, but implemented slightly differently, with some internal calls no longer fully compatible with current versions of the NGINX 1.29.x core.
The adaptation, therefore, required a detailed review of the parsing and header management functions in ngx_http_v3_request.c, respecting the internal logic of the HTTP/3 (QUIC) module and ensuring that the initialization of the structure r->headers_in occurred in a consistent and thread-safe manner.

After an initial testing cycle in an isolated environment, we verified the correct functioning of the $http_host even in the presence of duplicate headers or non-canonical values. Subsequently, the patch was ported to Controlled production on one of our high-traffic WordPress hosting clusters, to evaluate its stability with real loads and simultaneous QUIC connections.
The result was completely positive: no regressions, no impact on performance and, above all, full compatibility with existing rewrite rules and routing logic.

Once the tests were consolidated, we proceeded to the fork of the official NGINX repository and sending the Pull Request #917, available at this address:


HTTP/3: initialize Host header from :authority to enable $http_host variable – Pull Request #917

The proposal contains the complete technical description, the motivation and the reference to RFC 9114, as well as a minimal but significant diff: the addition of the function ngx_http_v3_set_host() and the adaptation of the validation logic between Host e :authority. This way, when the header Host he is absent but :authority is present (as expected by HTTP/3), NGINX automatically takes care of setting up $http_host consistently, aligning the behavior with that already consolidated in HTTP/1.1 and HTTP/2.

The patch is currently in progress "Open", pending review by the NGINX development team. As per practice, the Contributor License Agreement (CLA) of F5 to ensure correct contribution attribution and compliance with project integration policies.
We expect the review to take place in the coming weeks, with possible inclusion in the next release of the stable series.

This type of intervention perfectly embodies the philosophy of Managed Server Srl: not limiting ourselves to using open source tools, but actively contributing to their improvement, sharing field experiences, real-world cases, and solutions that arise from daily production needs.
We are therefore waiting for the official merge into the NGINX master branch, certain that this small but important fix will simplify the lives of many system administrators and developers who are adopting HTTP/3 in their application stacks.

Do you have doubts? Don't know where to start? Contact us!

We have all the answers to your questions to help you make the right choice.

Chat with us

Chat directly with our presales support.

0256569681

Contact us by phone during office hours 9:30 - 19:30

Contact us online

Open a request directly in the contact area.

DISCLAIMER, Legal Notes and Copyright. RedHat, Inc. holds the rights to Red Hat®, RHEL®, RedHat Linux®, and CentOS®; AlmaLinux™ is a trademark of the AlmaLinux OS Foundation; Rocky Linux® is a registered trademark of the Rocky Linux Foundation; SUSE® is a registered trademark of SUSE LLC; Canonical Ltd. holds the rights to Ubuntu®; Software in the Public Interest, Inc. holds the rights to Debian®; Linus Torvalds holds the rights to Linux®; FreeBSD® is a registered trademark of The FreeBSD Foundation; NetBSD® is a registered trademark of The NetBSD Foundation; OpenBSD® is a registered trademark of Theo de Raadt; Oracle Corporation holds the rights to Oracle®, MySQL®, MyRocks®, VirtualBox®, and ZFS®; Percona® is a registered trademark of Percona LLC; MariaDB® is a registered trademark of MariaDB Corporation Ab; PostgreSQL® is a registered trademark of PostgreSQL Global Development Group; SQLite® is a registered trademark of Hipp, Wyrick & Company, Inc.; KeyDB® is a registered trademark of EQ Alpha Technology Ltd.; Typesense® is a registered trademark of Typesense Inc.; REDIS® is a registered trademark of Redis Labs Ltd; F5 Networks, Inc. owns the rights to NGINX® and NGINX Plus®; Varnish® is a registered trademark of Varnish Software AB; HAProxy® is a registered trademark of HAProxy Technologies LLC; Traefik® is a registered trademark of Traefik Labs; Envoy® is a registered trademark of CNCF; Adobe Inc. owns the rights to Magento®; PrestaShop® is a registered trademark of PrestaShop SA; OpenCart® is a registered trademark of OpenCart Limited; Automattic Inc. holds the rights to WordPress®, WooCommerce®, and JetPack®; Open Source Matters, Inc. owns the rights to Joomla®; Dries Buytaert owns the rights to Drupal®; Shopify® is a registered trademark of Shopify Inc.; BigCommerce® is a registered trademark of BigCommerce Pty. Ltd.; TYPO3® is a registered trademark of the TYPO3 Association; Ghost® is a registered trademark of the Ghost Foundation; Amazon Web Services, Inc. owns the rights to AWS® and Amazon SES®; Google LLC owns the rights to Google Cloud™, Chrome™, and Google Kubernetes Engine™; Alibaba Cloud® is a registered trademark of Alibaba Group Holding Limited; DigitalOcean® is a registered trademark of DigitalOcean, LLC; Linode® is a registered trademark of Linode, LLC; Vultr® is a registered trademark of The Constant Company, LLC; Akamai® is a registered trademark of Akamai Technologies, Inc.; Fastly® is a registered trademark of Fastly, Inc.; Let's Encrypt® is a registered trademark of the Internet Security Research Group; Microsoft Corporation owns the rights to Microsoft®, Azure®, Windows®, Office®, and Internet Explorer®; Mozilla Foundation owns the rights to Firefox®; Apache® is a registered trademark of The Apache Software Foundation; Apache Tomcat® is a registered trademark of The Apache Software Foundation; PHP® is a registered trademark of the PHP Group; Docker® is a registered trademark of Docker, Inc.; Kubernetes® is a registered trademark of The Linux Foundation; OpenShift® is a registered trademark of Red Hat, Inc.; Podman® is a registered trademark of Red Hat, Inc.; Proxmox® is a registered trademark of Proxmox Server Solutions GmbH; VMware® is a registered trademark of Broadcom Inc.; CloudFlare® is a registered trademark of Cloudflare, Inc.; NETSCOUT® is a registered trademark of NETSCOUT Systems Inc.; ElasticSearch®, LogStash®, and Kibana® are registered trademarks of Elastic NV; Grafana® is a registered trademark of Grafana Labs; Prometheus® is a registered trademark of The Linux Foundation; Zabbix® is a registered trademark of Zabbix LLC; Datadog® is a registered trademark of Datadog, Inc.; Ceph® is a registered trademark of Red Hat, Inc.; MinIO® is a registered trademark of MinIO, Inc.; Mailgun® is a registered trademark of Mailgun Technologies, Inc.; SendGrid® is a registered trademark of Twilio Inc.; Postmark® is a registered trademark of ActiveCampaign, LLC; cPanel®, LLC owns the rights to cPanel®; Plesk® is a registered trademark of Plesk International GmbH; Hetzner® is a registered trademark of Hetzner Online GmbH; OVHcloud® is a registered trademark of OVH Groupe SAS; Terraform® is a registered trademark of HashiCorp, Inc.; Ansible® is a registered trademark of Red Hat, Inc.; cURL® is a registered trademark of Daniel Stenberg; Facebook®, Inc. owns the rights to Facebook®, Messenger® and Instagram®. This site is not affiliated with, sponsored by, or otherwise associated with any of the above-mentioned entities and does not represent any of these entities in any way. All rights to the brands and product names mentioned are the property of their respective copyright holders. All other trademarks mentioned are the property of their respective registrants.

JUST A MOMENT !

Have you ever wondered if your hosting sucks?

Find out now if your hosting provider is hurting you with a slow website worthy of 1990! Instant results.

Close the CTA
Back to top