IL-0253 Mozilla · 2022-01-13gcpnetworking
Firefox froze and could not load any web content for close to two hours, stuck in an HTTP/3 request to Telemetry
Firefox relies on Mozilla-run services for updates, telemetry, certificate management and crash reporting, hosted by several cloud providers behind load balancers. On Google Cloud Platform these load balancers have an HTTP/3 setting with the states "Enabled", "Disabled" and "Automatic (default)", and Mozilla's were on "Automatic (default)". When GCP made HTTP/3 the default, Firefox, which uses HTTP/3 whenever a server supports it, began sending some requests to these services over HTTP/3. The most critical services, such as updates, use a beConservative flag that keeps experimental technology such as HTTP/3 out of their connections.
All HTTP/3 traffic goes through Necko, the Firefox networking stack. Rust components reach Necko through an intermediate library called viaduct, which lower-cases every header. For HTTP/3 uploads, the higher-level Necko API checks for a Content-Length header without regard to case and adds one if it is missing, while the lower-level HTTP/3 code looks it up case-sensitively. Telemetry was the only Rust component in Firefox Desktop that used the network stack and added a Content-Length header. The HTTP/3 code did not find the lower-cased header, considered the request complete while its body was still unsent, and then looped forever because it only stopped when nothing was left to send. The loop ran on the single socket thread, so the whole browser lost network access.
Mozilla explicitly disabled HTTP/3 on its GCP load balancers at 09:12 UTC, which unblocked users before the root cause was understood. Mozilla named three contributing factors: GCP's change was not announced, the "Automatic (default)" setting let it apply automatically, and the continuous integration system did not cover HTTP/3 combined with viaduct on Firefox Desktop, since the HTTP/3 tests covered the protocol and Necko only as web content uses them.
- Severity
- –
- Scope
- Firefox could not load web content; the browser hung on its single network socket thread
- Users affected
- Firefox users worldwide
- Region
- worldwide
- Time to detect
- –
- Time to resolve
- 1h 44m
Symptoms
On 13 January 2022, shortly after 07:28 UTC, Mozilla saw a spike in crashes reported through the Firefox crash reporter and received several reports, from inside and outside Mozilla, of the browser hanging. For close to two hours Firefox was unusable for users worldwide and could not load web content.
The backlog of unprocessed crash reports grew to close to 300K. Incident response found that the browser was hanging inside a network request to one of the Firefox internal services. All network requests in Firefox go through one socket thread, so that one stuck request blocked every other network request. Users who had disabled Telemetry did not see the problem.
The team first looked for a change of its own and found that no updates or configuration changes had shipped. HTTP/3 had been enabled since Firefox 88 and was in active use on popular websites, so it did not look like a new factor. Suspecting an invisible change by a cloud provider, the team checked its load balancer settings, which had not changed, and then found in logs that the load balancers of the Telemetry service were serving HTTP/3 connections, which they had not done before. Tests with various tools and remote services, even against the Telemetry staging server, did not reproduce the hang; only Firefox itself did, against the staging server.
Symptom Service outage
Root cause
Root cause Software defect
- Trigger
- At 07:28 UTC on 13 January 2022 GCP deployed an unannounced change that made HTTP/3 the default for load balancers set to "Automatic (default)", so some Firefox connections to Mozilla's services, including Telemetry, started using HTTP/3 instead of HTTP/2.
Detection
A spike in crashes reported through the Firefox crash reporter, and reports of browser hangs from inside and outside Mozilla.
Remediation
- Explicitly disabled HTTP/3 on the GCP load balancers at 09:12 UTC
Action items
- Work with GCP to improve the situation around unannounced changes such as the HTTP/3 default
- Review all service configurations to avoid similar mistakes
- Run more system tests with different HTTP versions
- Find ways to make the browser more resilient to such problems and to speed up incident response
Lessons
The hang needed three things together: a provider-side change to the HTTP version, a Rust component calling the network stack through viaduct, and a case-sensitive header lookup deep in the HTTP/3 code. Leaving the load balancers on an automatic default let the provider's change apply without any action from Mozilla, and an announcement would likely have led to a controlled test in staging first. A change of HTTP version and an extra networking layer such as viaduct are significant enough to be covered by system tests.
Sources
Related cases
IL-0150A latent race condition in DynamoDB's DNS automation left an empty DNS record for dynamodb.us-east-1.amazonaws.com, and the DynamoDB and EC2 launch failures that followed caused EKS, ECS and Fargate container launch failures and cluster scaling delays in us-east-1Amazon Web Services · 2025same root cause · shared Cloud load balancer
IL-0352Heroku Router 2.0 spread HTTP requests unevenly across dynos for four days, causing latency and timeoutsHeroku · 2025same root cause · shared Cloud load balancer
IL-0131A new kind of GenerateContent request made Vertex AI prediction API servers segfault, and once enough of them restarted at the same time the serving fleet could not carry the loadGoogle Cloud · 2024same root cause · shared Cloud load balancer
IL-0529New Google Cloud VMs and other instances came up without network connectivity in all zones after VPC controllers stoppedGoogle Cloud · 2024same root cause · shared Cloud load balancer
IL-0243Google's global External Proxy Load Balancing returned 404 errors for most HTTP/S endpoints for 33 minutesGoogle Cloud · 2021same root cause · shared Cloud load balancer
IL-0106A malformed Traefik IngressRoute triggered a Traefik v2 bug once a node rollover restarted every Traefik pod, and all Kubernetes services returned 404DFDS · 2021same root cause · shared Cloud load balancer
IL-0493Netlify's Google Cloud TCP load balancer stopped serving about 5% of global traffic as all backends failed health checksNetlify · 2021same root cause · shared Cloud load balancer
IL-0345Many Render services in the Oregon region returned 503 errors for about four hoursRender · 2020same root cause · shared Cloud load balancer
IL-0030Most nodes of a cluster went NotReady together for 15 minutes while the API server sat behind an AWS ELBZalando · 2019same root cause · shared Cloud load balancer
IL-0338Google Cloud HTTP(S) and TCP/SSL Proxy load balancers returned 502s or reset connections on 33% to 87% of requestsGoogle Cloud · 2018same root cause · shared Cloud load balancer
IL-0395Google Cloud HTTP(S) Load Balancer gave 502s on 25% of requests for 22 minutes after reverting to a stale configurationGoogle Cloud · 2017same root cause · shared Cloud load balancer
IL-0396New or live-migrated GCE instances, VPN tunnels and network load balancers were unreachable on public IPs for over 2hGoogle Cloud · 2017same root cause · shared Cloud load balancer
Check your cluster for this
For AI tools and scripts
- This case as JSON: https://incidentlib-api.benchlet.com/cases/IL-0253
- The cases most like it: https://incidentlib-api.benchlet.com/cases/IL-0253/similar
- MCP server (Streamable HTTP, no key):
https://incidentlib-mcp.benchlet.com/mcp
The API needs no key. The MCP server offers search_incidents, get_incident, list_incidents, get_topic and vocab; both are explained on API and MCP.