dotnet/yarp · error · HttpRequestException
SPDY requests require HTTP/1.1 support, but outbound HTTP/1.
Error message
SPDY requests require HTTP/1.1 support, but outbound HTTP/1.1 was disallowed by HttpVersionPolicy.
What it means
YARP must send SPDY upgrade requests over HTTP/1.1 because the SPDY protocol has no HTTP/2 equivalent path. The forwarder checks whether HTTP/1.1 is permitted by the cluster's Version/VersionPolicy before issuing the upgrade. If the policy forces HTTP/2-or-higher only (RequestVersionOrHigher or RequestVersionExact with a Version whose major >= 2), the upgrade cannot proceed and this HttpRequestException is thrown at request time.
Source
Thrown at src/ReverseProxy/Forwarder/HttpForwarder.cs:414
outgoingConnect = true;
tryDowngradingH2WsOnFailure = true;
break;
default:
// Override to use HTTP/1.1, nothing else is supported.
outgoingUpgrade = true;
break;
}
}
bool http1IsAllowed = outgoingPolicy == HttpVersionPolicy.RequestVersionOrLower || outgoingVersion.Major == 1;
if (outgoingUpgrade)
{
// Can only be done on HTTP/1.1, throw if disallowed by options.
if (!http1IsAllowed)
{
throw new HttpRequestException(isSpdyRequest
? "SPDY requests require HTTP/1.1 support, but outbound HTTP/1.1 was disallowed by HttpVersionPolicy."
: "An outgoing HTTP/1.1 Upgrade request is required to proxy this request, but is disallowed by HttpVersionPolicy.");
}
destinationRequest.Version = HttpVersion.Version11;
destinationRequest.VersionPolicy = HttpVersionPolicy.RequestVersionOrLower;
destinationRequest.Method = HttpMethod.Get;
}
else if (outgoingConnect)
{
// HTTP/2 only (for now).
destinationRequest.Version = HttpVersion.Version20;
destinationRequest.VersionPolicy = HttpVersionPolicy.RequestVersionExact;
destinationRequest.Method = HttpMethod.Connect;
destinationRequest.Headers.Protocol = connectProtocol ?? WebSocketName;
tryDowngradingH2WsOnFailure &= http1IsAllowed;
}
elseView on GitHub (pinned to bd11867bee)
Solutions
- Set `VersionPolicy` to `RequestVersionOrLower` (the default) for the cluster or ForwarderRequestConfig so HTTP/1.1 is permitted for upgrade requests while still preferring HTTP/2.
- Remove the explicit `Version`/`VersionPolicy` override on the cluster so YARP falls back to its defaults (Version=HTTP/2, Policy=RequestVersionOrLower), which allows HTTP/1.1.
- Filter or reject SPDY upgrade requests at an earlier middleware or load balancer layer if SPDY support is not needed, rather than forcing a protocol policy that breaks all upgrades.
- If the client should not be sending SPDY at all, investigate and fix the upstream client configuration that emits the `Upgrade: SPDY/` header.
Example fix
// before (appsettings.json — breaks SPDY/WebSocket upgrades)
"HttpRequest": {
"Version": "2.0",
"VersionPolicy": "RequestVersionOrHigher"
}
// after — allows HTTP/1.1 downgrade for upgrade requests
"HttpRequest": {
"Version": "2.0",
"VersionPolicy": "RequestVersionOrLower"
} Defensive patterns
Strategy: validation
Validate before calling
// Before calling forwarder.SendAsync, verify the config allows HTTP/1.1
var version = requestConfig?.Version ?? new Version(2, 0);
var policy = requestConfig?.VersionPolicy ?? HttpVersionPolicy.RequestVersionOrLower;
bool http1Allowed = policy == HttpVersionPolicy.RequestVersionOrLower || version.Major == 1;
if (!http1Allowed && context.Request.Headers.Upgrade.ToString().StartsWith("SPDY/", StringComparison.OrdinalIgnoreCase))
{
context.Response.StatusCode = 502;
return;
} Type guard
static bool AllowsHttp1Upgrade(ForwarderRequestConfig? config)
{
var version = config?.Version ?? new Version(2, 0);
var policy = config?.VersionPolicy ?? HttpVersionPolicy.RequestVersionOrLower;
return policy == HttpVersionPolicy.RequestVersionOrLower || version.Major == 1;
} Try / catch
try { await forwarder.SendAsync(context, prefix, client, config, transformer, ct); }
catch (HttpRequestException ex) when (ex.Message.Contains("SPDY"))
{ context.Response.StatusCode = 502; logger.LogWarning("SPDY upgrade rejected by policy"); } Prevention
- Never set VersionPolicy to RequestVersionExact or RequestVersionOrHigher with HTTP/2+ for clusters that receive upgrade requests.
- Keep the default RequestVersionOrLower unless you have a specific reason to force a protocol version.
- Document SPDY incompatibility in cluster configs that pin HTTP/2.
When it happens
Trigger: An incoming client request carries an `Upgrade: SPDY/x.y` header on an upgradable connection (`IHttpUpgradeFeature.IsUpgradableRequest` is true). The effective outgoing config has `Version.Major >= 2` combined with `VersionPolicy` set to `RequestVersionOrHigher` or `RequestVersionExact`, making `http1IsAllowed` evaluate to false. This typically means a cluster-level or per-call `ForwarderRequestConfig` explicitly pins the protocol to HTTP/2 or HTTP/3 without allowing downgrade.
Common situations: An operator pins all outbound traffic to HTTP/2 (e.g., `{ "Version": "2.0", "VersionPolicy": "RequestVersionOrHigher" }` in appsettings.json) and a legacy client or monitoring tool issues an SPDY upgrade request. SPDY is obsolete and most real occurrences come from old tooling or misconfigured clients that still send SPDY upgrade headers.
Related errors
- An outgoing HTTP/1.1 Upgrade request is required to proxy th
- Configuration Filter Error: Substitution for '{lookup}' in c
- A non-empty CustomTransform value is required
- A non-empty CustomMetadata value is required
- The route config format has changed, routes are now objects
AI-assisted analysis of dotnet/yarp@bd11867bee (2026-08-13).
Data as JSON: /api/errors/b80efa7f67bc10d3.
Report an issue: GitHub.