dotnet/yarp · error · HttpRequestException

An outgoing HTTP/1.1 Upgrade request is required to proxy th

Error message

An outgoing HTTP/1.1 Upgrade request is required to proxy this request, but is disallowed by HttpVersionPolicy.

What it means

WebSocket (and other non-SPDY) upgrade requests that cannot use HTTP/2 extended CONNECT fall back to a classic HTTP/1.1 Upgrade handshake. If the outgoing protocol policy disallows HTTP/1.1, YARP cannot proxy the WebSocket connection and throws this HttpRequestException. The decision tree in CreateRequestMessageAsync selects `outgoingUpgrade = true` for the HTTP/1.1 fallback path, then checks the same `http1IsAllowed` gate.

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;
        }
        else

View on GitHub (pinned to bd11867bee)

Solutions

  1. Set `VersionPolicy` to `RequestVersionOrLower` so YARP can downgrade to HTTP/1.1 for WebSocket upgrade requests.
  2. If using HTTPS to the destination, ensure the version/policy combo selects the H2WS extended-CONNECT path instead (e.g., Version=2 + RequestVersionOrLower with `https://` destination prefix).
  3. Remove the Version/VersionPolicy override entirely so defaults apply (RequestVersionOrLower, which permits HTTP/1.1).
  4. Switch the destination prefix from `http://` to `https://` if the backend supports HTTP/2 WebSockets over TLS.

Example fix

// before — WebSocket upgrade blocked
var config = new ForwarderRequestConfig
{
    Version = new Version(2, 0),
    VersionPolicy = HttpVersionPolicy.RequestVersionExact // no HTTP/1.1 fallback
};
// after — allows HTTP/1.1 downgrade for WebSocket upgrades
var config = new ForwarderRequestConfig
{
    Version = new Version(2, 0),
    VersionPolicy = HttpVersionPolicy.RequestVersionOrLower
};
Defensive patterns

Strategy: validation

Validate before calling

// Validate that WebSocket-capable routes allow HTTP/1.1 fallback
var version = requestConfig?.Version ?? new Version(2, 0);
var policy = requestConfig?.VersionPolicy ?? HttpVersionPolicy.RequestVersionOrLower;
bool http1Allowed = policy == HttpVersionPolicy.RequestVersionOrLower || version.Major == 1;
if (!http1Allowed)
{
    logger.LogWarning("Cluster config blocks WebSocket upgrades (no HTTP/1.1 fallback)");
}

Type guard

static bool SupportsWebSocketUpgrade(Version? version, HttpVersionPolicy? policy, bool isHttps)
{
    var v = version ?? new Version(2, 0);
    var p = policy ?? HttpVersionPolicy.RequestVersionOrLower;
    // H2WS via extended CONNECT, or HTTP/1.1 upgrade allowed
    return (v.Major >= 2 && (p == HttpVersionPolicy.RequestVersionOrLower || isHttps))
        || p == HttpVersionPolicy.RequestVersionOrLower
        || v.Major == 1;
}

Try / catch

try { await forwarder.SendAsync(context, prefix, client, config, transformer, ct); }
catch (HttpRequestException ex) when (ex.Message.Contains("Upgrade request is required"))
{ context.Response.StatusCode = 502; await context.Response.WriteAsync("WebSocket upgrade not supported with current protocol policy"); }

Prevention

When it happens

Trigger: An incoming WebSocket upgrade request (`IHttpUpgradeFeature.IsUpgradableRequest` with `Upgrade: websocket`) reaches a cluster whose Version/VersionPolicy combination routes to the `default` case of the WebSocket switch (lines 400-404), setting `outgoingUpgrade = true`. Simultaneously the policy forbids HTTP/1.1 (`outgoingPolicy != RequestVersionOrLower && outgoingVersion.Major != 1`). For example: Version=2.0, VersionPolicy=RequestVersionExact, and a non-HTTPS destination prefix.

Common situations: A developer configures a cluster to use HTTP/2 over plain HTTP (`http://`) with `RequestVersionExact` for performance, not realizing this blocks WebSocket upgrades. Or an operator sets `RequestVersionOrHigher` with HTTP/2 for a plain-HTTP backend that doesn't support HTTP/2 extended CONNECT for WebSockets.

Related errors


AI-assisted analysis of dotnet/yarp@bd11867bee (2026-08-13). Data as JSON: /api/errors/5df89fb0832b871f. Report an issue: GitHub.