Skip to content

Starlette BadHost (CVE-2026-48710): one Host header bypasses auth in FastAPI, vLLM, MCP

X41 D-Sec discloses CVE-2026-48710 in Starlette <1.0.1: a Host-header re-parse desync that lets attackers forge request.url.path. Upgrade to 1.0.1.

Published 4 min read

X41 D-Sec disclosed CVE-2026-48710 — dubbed BadHost — in Starlette < 1.0.1, the ASGI framework that backs FastAPI, vLLM, LiteLLM, and most Python-based MCP servers. A single character in the HTTP Host header lets an unauthenticated attacker bypass any auth middleware that gates routes by request.url.path. Patched in Starlette 1.0.1, released 21 May 2026, with public disclosure on 22 May.

The primary advisory is on GitHub: GHSA-86qp-5c8j-p5mr. NVD entry: CVE-2026-48710.

What the bug does

Starlette builds request.url by concatenating the client-supplied Host header with the request path, then re-parsing the result as a URL. The Host header is never validated against the RFC 9112 §3.2 / RFC 3986 §3.2.2 grammar before that re-parse. A Host containing /, ?, or # shifts the path/query/fragment boundaries during reconstruction — so request.url.path no longer matches the wire path the ASGI server actually routed against.

The router dispatches on the raw scope path. Middleware sees the poisoned, re-parsed path. Any path-based decision in middleware — if request.url.path.startswith("/admin"): require_auth() — can be made to lie while the underlying route still executes.

Affected versions

  • Starlette < 1.0.1. Every version from the initial release through 1.0.0 is vulnerable.
  • FastAPI inherits Starlette's request object — installations pulling Starlette < 1.0.1 are exposed.
  • vLLM, LiteLLM, and OpenAI-compatible API proxies that pin Starlette via their own dependency graph.
  • MCP servers built on Python ASGI stacks. These are especially exposed because the MCP spec defines unauthenticated OAuth discovery endpoints at fixed paths; a Host-header trick can re-aim middleware away from a protected route while the protected handler still runs.

The fix commit is 764dab0 on Kludex/starlette. The patch validates the Host value and falls back to scope["server"] for any malformed input.

Exploitation status

No in-the-wild exploitation reported by the vendor, X41, or OSTIF at the time of disclosure. A working PoC ships with the X41 research repository at github.com/x41sec/poc/tree/master/starlette-host-header, including a runtime check that confirms exposure on a deployed instance.

X41 also published detection artifacts. From the BadHost advisory:

  • Online scanner at badhost.org — sends a probe Host header and reports whether request.url.path desyncs from the routed path.
  • Semgrep rules and CodeQL queries in the same repository, intended for source-code audits to flag middleware that reads request.url.path for auth decisions instead of scope["path"].

Action checklist

  1. Upgrade Starlette to 1.0.1 in every Python service. pip install -U starlette>=1.0.1 and rebuild affected images. FastAPI users: bump FastAPI to a release that pins Starlette >=1.0.1, or override the pin yourself.
  2. Audit middleware for request.url.path reads. Search your codebase for request.url, request.url.path, and url.path references inside auth middleware. Replace with request.scope["path"] if the decision needs to match the routed path.
  3. Run the X41 scanner against every Starlette-fronted endpoint reachable from the internet — including LLM inference proxies, MCP servers, and internal admin panels. The scanner returns a boolean and is fast enough to walk a fleet.
  4. Review reverse-proxy Host handling. Some proxies (nginx, Envoy, Caddy) will reject Host values containing / by default. If yours doesn't, add a rejection rule as defence in depth — the proxy is the only spot where you can kill the malformed header before it reaches Python.
  5. Check audit logs for crafted Host headers from the past 30 days. Look for entries whose Host value contains /, ?, or #. Any such request that returned 200 against a path that should have required auth is worth a manual review.

Context

The disclosure timeline is uncomfortable. X41 reported the bug to Starlette maintainers on 4 February 2026 with a working PoC, after discovering it during an OSTIF-sponsored audit of vLLM that began 27 January. A patch was proposed in early March. The fix did not ship until 21 May — eleven weeks. Public disclosure followed one day later, leaving operators effectively zero lead time.

The blast radius is unusually wide because Starlette is invisible plumbing: most teams running FastAPI, vLLM, or an MCP server have never opened a Starlette file. They pulled it as a transitive dep two layers down and never thought about it again. This is the third time in twelve months a host-header-style ASGI bug has surfaced (Mangum, h11, now Starlette); the pattern is becoming a category, not an incident.

For MCP servers specifically, the exposure is structural. Path-based auth in front of well-known/oauth-authorization-server discovery endpoints — the most common gating pattern in current MCP deployments — is exactly what BadHost defeats. If you ship an MCP server, treat this as the impetus to move auth into a request-handler decorator and out of middleware path checks.

Sources: OSTIF disclosure, CSO Online, InfoWorld, badhost.org.

Related stories