BadHost (CVE-2026-48710): one Host-header character bypasses auth in Starlette, vLLM and FastMCP
X41 D-Sec disclosed on May 22, 2026 a critical auth bypass in Starlette < 1.0.1. A single / ? or # in the HTTP Host header desynchronises the routed path from the path the middleware sees, breaking path-based authorization in vLLM, LiteLLM, FastMCP and thousands of FastAPI-based AI agents.
What is this?
On May 22, 2026, German security firm X41 D-Sec publicly disclosed CVE-2026-48710, nicknamed BadHost: an unauthenticated authorization bypass in the Starlette Python ASGI framework, the routing core of FastAPI. The flaw was discovered on January 27, 2026 during an OSTIF-sponsored audit of the vLLM inference server, reported to Starlette maintainers on February 4, and patched on May 21, 2026 with Starlette 1.0.1 — one day before public disclosure.
Starlette ships at roughly 325 million downloads per week. Through FastAPI it underlies a very large slice of the modern AI infrastructure stack: vLLM, LiteLLM, Text Generation Inference, OpenAI-compatible API proxies, MCP servers (including FastMCP), agent control planes, and model-serving dashboards. All versions from 0.8.3 through 1.0.0 are affected.
The bug is small, the blast radius is large, and the timing window between patch and disclosure was effectively zero.
How it works
Starlette reconstructs request.url by concatenating the HTTP Host header with the request path and re-parsing the result as a URL. The Host value is not validated against the RFC 9112 / RFC 3986 grammar before reconstruction. Any byte the client sends in Host: flows directly into the URL parser.
If an attacker injects /, ? or # into the Host header, the boundaries between authority, path, query and fragment shift during re-parse. The path the router sees (taken from the ASGI scope, derived from the HTTP request line) and the path the middleware sees (request.url.path) diverge. Any path-based authorization decision made in middleware is now applied to a different string than the one the router dispatches against.
# Conceptual sketch of the desynchronization, based on the
# public May 22, 2026 disclosure. No payload against any live
# system is reproduced here.
Wire: GET /admin HTTP/1.1
Host: foo?
ASGI scope (truth):
scope["path"] = "/admin" # router dispatches here
Starlette reconstruction:
request.url = "http://foo?/admin"
request.url.path = "/" # middleware sees this
Middleware:
if request.url.path.startswith("/admin"): # False
require_auth()
# auth skipped
Router:
dispatch("/admin") # endpoint runs
The net effect: a request that would normally return 403 Forbidden on a protected route returns 200 OK when a single rogue character is appended to the Host header, while the protected handler still executes.
The pattern is generic. It does not matter whether the middleware is a third-party library or a hand-rolled BaseHTTPMiddleware; what matters is whether it reads request.url.path (poisoned) or scope["path"] (truth) when making a security decision.
Why it matters
Three properties make BadHost worth careful attention in an AI security context.
First, the cascade. Starlette is not a niche dependency. FastAPI sits on top of it, and FastAPI is the de-facto framework for shipping Python model servers and agent control planes in 2025-2026. X41 and OSTIF explicitly list vLLM, LiteLLM, OpenAI-compatible proxies, and MCP servers among the affected projects. Anyone running a self-hosted inference endpoint, an internal LLM gateway, or a FastMCP server behind a path-based ACL has to assume vulnerability until patched and audited.
Second, MCP servers are particularly exposed. The Model Context Protocol specification mandates unauthenticated OAuth discovery endpoints; those routes are reliably reachable without credentials and sit next to administrative routes that are supposed to be authenticated. That layout is a textbook setup for an authorization bypass that pivots from a public route to a protected one through middleware confusion.
Third, the disclosure timing. The vulnerability was reported to upstream in early February. A patch was proposed in early March. The fix only shipped on May 21, and X41 published full details the next day — practical lead time for operators was a matter of hours, not the customary days or weeks. For widely-embedded libraries this is unusually tight, and it means defenders should assume scanning and opportunistic exploitation are already happening.
Defenses
The mitigation path is short and concrete.
Upgrade Starlette to 1.0.1 or later. This is the minimum step and resolves the underlying parsing issue. FastAPI, vLLM, LiteLLM, FastMCP and most agent frameworks depend on Starlette transitively; pin the dependency and rebuild containers. The patch validates the Host header against the RFC 9112 / 3986 grammar and falls back to scope["server"] for malformed values.
Move security decisions off request.url.path and onto scope["path"] in any custom middleware. The ASGI scope path comes from the HTTP request line and cannot be poisoned via Host injection. This is the architecturally correct fix and should outlive any single CVE: never trust a value derived from a client-controlled header for an authorization choice.
Audit your own middleware now, including any BaseHTTPMiddleware subclass and raw ASGI middleware in your stack. X41 has published a free remote scanner at badhost.org, plus Semgrep rules and CodeQL queries on GitHub for static detection of vulnerable patterns. Run them against every internal FastAPI service, model server and MCP host.
Add a reverse-proxy guard. Configure NGINX, Envoy, Traefik or your ingress to reject requests whose Host header contains /, ?, #, whitespace or any byte outside the RFC 9112 authority grammar. This is a useful belt-and-braces measure against the next variant of the same bug class.
Treat MCP/FastMCP servers as production attack surface. If your MCP host runs alongside admin endpoints, isolate them behind a separate network plane, require client-certificate or mTLS auth on administrative routes, and do not rely on path prefixes alone for authorization. The MCP spec’s mandatory unauthenticated discovery endpoints make path-based ACLs fragile by design.
Re-test the broader class. BadHost is a header-derived URL reconstruction bug. The same family of issues has hit many web frameworks over the years (Django ALLOWED_HOSTS, Rack, Express). Treat any place your stack rebuilds a URL from request headers and then makes a trust decision on it as a candidate for review.
Status
| Item | Reference | Date | Notes |
|---|---|---|---|
| Discovery | X41 D-Sec, OSTIF-funded vLLM audit | 2026-01-27 | Found during external audit |
| Upstream report | Starlette maintainers notified | 2026-02-04 | With working PoC |
| Patch proposed | Pull request opened | 2026-03-01 | ~3 months until release |
| Fixed release | Starlette 1.0.1 | 2026-05-21 | Validates Host against RFC 9112/3986 |
| Public disclosure | X41 D-Sec advisory + badhost.org | 2026-05-22 | CVE-2026-48710 published |
| Affected versions | Starlette 0.8.3 → 1.0.0 inclusive | — | All FastAPI builds on those |
| Affected ecosystem | FastAPI, vLLM, LiteLLM, Text Generation Inference, FastMCP, MCP servers | — | Plus thousands of agent frameworks |
| Scanner / rules | badhost.org, x41sec/poc GitHub repo | 2026-05-22 | Semgrep + CodeQL |
The lesson is one we keep relearning: when an AI stack inherits a framework bug, the AI doesn’t add new defenses — it only multiplies the number of internet-exposed deployments. Patch fast, switch your middleware to scope["path"], and stop trusting Host for anything that gates access.
Sources
- → https://badhost.org/
- → https://ostif.org/disclosing-the-badhost-vulnerability-in-starlette/
- → https://news.ycombinator.com/item?id=48277107
- → https://www.cyberkendra.com/2026/05/badhost-cve-2026-48710-one-rogue-header.html
- → https://mlq.ai/news/critical-badhost-flaw-in-starlette-exposes-millions-of-ai-agent-deployments-to-auth-bypass/
- → https://gigazine.net/gsc_news/en/20260527-millions-ai-agents-imperiled-vulnerability-starlette/