{"record":{"id":"b80efa7f67bc10d3","repo":"dotnet/yarp","slug":"spdy-requests-require-http-1-1-support-but-outbou","errorCode":null,"errorMessage":"SPDY requests require HTTP/1.1 support, but outbound HTTP/1.1 was disallowed by HttpVersionPolicy.","messagePattern":"SPDY requests require HTTP/1\\.1 support, but outbound HTTP/1\\.1 was disallowed by HttpVersionPolicy\\.","errorType":"exception","errorClass":"HttpRequestException","httpStatus":null,"severity":"error","filePath":"src/ReverseProxy/Forwarder/HttpForwarder.cs","lineNumber":414,"sourceCode":"                    outgoingConnect = true;\n                    tryDowngradingH2WsOnFailure = true;\n                    break;\n\n                default:\n                    // Override to use HTTP/1.1, nothing else is supported.\n                    outgoingUpgrade = true;\n                    break;\n            }\n        }\n\n        bool http1IsAllowed = outgoingPolicy == HttpVersionPolicy.RequestVersionOrLower || outgoingVersion.Major == 1;\n\n        if (outgoingUpgrade)\n        {\n            // Can only be done on HTTP/1.1, throw if disallowed by options.\n            if (!http1IsAllowed)\n            {\n                throw new HttpRequestException(isSpdyRequest\n                    ? \"SPDY requests require HTTP/1.1 support, but outbound HTTP/1.1 was disallowed by HttpVersionPolicy.\"\n                    : \"An outgoing HTTP/1.1 Upgrade request is required to proxy this request, but is disallowed by HttpVersionPolicy.\");\n            }\n\n            destinationRequest.Version = HttpVersion.Version11;\n            destinationRequest.VersionPolicy = HttpVersionPolicy.RequestVersionOrLower;\n            destinationRequest.Method = HttpMethod.Get;\n        }\n        else if (outgoingConnect)\n        {\n            // HTTP/2 only (for now).\n            destinationRequest.Version = HttpVersion.Version20;\n            destinationRequest.VersionPolicy = HttpVersionPolicy.RequestVersionExact;\n            destinationRequest.Method = HttpMethod.Connect;\n            destinationRequest.Headers.Protocol = connectProtocol ?? WebSocketName;\n            tryDowngradingH2WsOnFailure &= http1IsAllowed;\n        }\n        else","sourceCodeStart":396,"sourceCodeEnd":432,"githubUrl":"https://github.com/dotnet/yarp/blob/bd11867bee7df522e7fd3effb08a9c85fd616908/src/ReverseProxy/Forwarder/HttpForwarder.cs#L396-L432","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (appsettings.json — breaks SPDY/WebSocket upgrades)\n\"HttpRequest\": {\n  \"Version\": \"2.0\",\n  \"VersionPolicy\": \"RequestVersionOrHigher\"\n}\n// after — allows HTTP/1.1 downgrade for upgrade requests\n\"HttpRequest\": {\n  \"Version\": \"2.0\",\n  \"VersionPolicy\": \"RequestVersionOrLower\"\n}","handlingStrategy":"validation","validationCode":"// Before calling forwarder.SendAsync, verify the config allows HTTP/1.1\nvar version = requestConfig?.Version ?? new Version(2, 0);\nvar policy = requestConfig?.VersionPolicy ?? HttpVersionPolicy.RequestVersionOrLower;\nbool http1Allowed = policy == HttpVersionPolicy.RequestVersionOrLower || version.Major == 1;\nif (!http1Allowed && context.Request.Headers.Upgrade.ToString().StartsWith(\"SPDY/\", StringComparison.OrdinalIgnoreCase))\n{\n    context.Response.StatusCode = 502;\n    return;\n}","typeGuard":"static bool AllowsHttp1Upgrade(ForwarderRequestConfig? config)\n{\n    var version = config?.Version ?? new Version(2, 0);\n    var policy = config?.VersionPolicy ?? HttpVersionPolicy.RequestVersionOrLower;\n    return policy == HttpVersionPolicy.RequestVersionOrLower || version.Major == 1;\n}","tryCatchPattern":"try { await forwarder.SendAsync(context, prefix, client, config, transformer, ct); }\ncatch (HttpRequestException ex) when (ex.Message.Contains(\"SPDY\"))\n{ context.Response.StatusCode = 502; logger.LogWarning(\"SPDY upgrade rejected by policy\"); }","preventionTips":["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."],"tags":["network","protocol","http2","upgrade","configuration"],"backgroundTag":null,"analyzedSha":"bd11867bee7df522e7fd3effb08a9c85fd616908","analyzedAt":"2026-08-13T21:29:49.359Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}