HTTP client configuration
Each cluster gets its own HTTP client for talking to its destinations - configure its connection, TLS, and per-request behavior independently of any other cluster.
One client per cluster
Every cluster has its own HttpMessageInvoker, used for every request forwarded to its destinations. On startup, every cluster gets a new one; if a cluster's configuration later changes, IForwarderHttpClientFactory decides whether the existing client can keep being used or a new one is needed - the default implementation creates a new one whenever HttpClientConfig itself has changed.
HttpClient settings
Configured under HttpClient on a cluster, using HttpClientConfig:
JSON"HttpClient": {
"SslProtocols": ["Tls12", "Tls13"],
"MaxConnectionsPerServer": "10",
"DangerousAcceptAnyServerCertificate": "false",
"RequestHeaderEncoding": "utf-8",
"ResponseHeaderEncoding": "utf-8",
"EnableMultipleHttp2Connections": "true",
"WebProxy": {
"Address": "http://myproxy:8080",
"BypassOnLocal": "true",
"UseDefaultCredentials": "false"
}
}SslProtocols— which TLS/SSL protocol versions this client accepts. No value is set by default.MaxConnectionsPerServer— maximum concurrent HTTP/1.1 connections to the same destination. Defaults toint32.MaxValue.DangerousAcceptAnyServerCertificate—truedisables all validation of the destination's TLS certificate. Defaults tofalse; the name is a deliberate warning, not a suggestion.RequestHeaderEncoding/ResponseHeaderEncoding— encoding (e.g."utf-8","iso-8859-1") used for non-ASCII header values on outgoing requests / incoming responses, viaSocketsHttpHandler's header encoding selectors.EnableMultipleHttp2Connections— allow opening additional HTTP/2 connections to the same destination once the existing ones hit their concurrent-stream limit. Defaults totrue.WebProxy— route outbound requests to destinations through an upstream HTTP proxy:Addressof the proxy,BypassOnLocalto skip it for local addresses,UseDefaultCredentialsto authenticate to it with the app's own credentials.
If you set a header encoding other than ASCII here, the server hosting YARP also needs to be told to accept it. For Kestrel, that means setting KestrelServerOptions.RequestHeaderEncodingSelector/ResponseHeaderEncodingSelector to match - otherwise Kestrel rejects the very headers this setting was meant to allow.
C#builder.WebHost.ConfigureKestrel(kestrel =>
{
kestrel.RequestHeaderEncodingSelector = _ => Encoding.Latin1;
kestrel.ResponseHeaderEncodingSelector = _ => Encoding.Latin1;
});HttpRequest settings
Configured under HttpRequest on a cluster, using ForwarderRequestConfig - these govern the outgoing request itself, not the underlying connection:
JSON"HttpRequest": {
"ActivityTimeout": "00:01:40",
"Version": "2",
"VersionPolicy": "RequestVersionOrLower",
"AllowResponseBuffering": "false"
}ActivityTimeout— how long a request can sit idle between operations before it's canceled. Defaults to 100 seconds; resets whenever response headers arrive or request/response/streaming data (gRPC, WebSockets) is read or written. TCP keep-alives and HTTP/2 pings don't reset it; WebSocket pings do.Version— the outgoing HTTP version:1.0,1.1,2, or3. Defaults to2.VersionPolicy— how the final version gets picked:RequestVersionOrLower(default),RequestVersionOrHigher, orRequestVersionExact.AllowResponseBuffering— allow write-buffering when sending the response back to the client, if the host supports it. Breaks server-sent events if enabled.
Two clusters with different HTTP settings
JSON{
"Clusters": {
"cluster1": {
"LoadBalancingPolicy": "Random",
"HttpClient": { "SslProtocols": ["Tls12"], "MaxConnectionsPerServer": "10" },
"HttpRequest": { "ActivityTimeout": "00:00:30" },
"Destinations": {
"cluster1/destination1": { "Address": "https://localhost:10000/" }
}
},
"cluster2": {
"HttpClient": { "SslProtocols": ["Tls12"] },
"HttpRequest": { "Version": "1.1", "VersionPolicy": "RequestVersionExact" },
"Destinations": {
"cluster2/destination1": { "Address": "https://localhost:10001/" }
}
}
}
}Configuring in code
The same settings apply when building clusters directly rather than from IConfiguration - assign an HttpClientConfig to ClusterConfig.HttpClient before passing the cluster to LoadFromMemory:
C#var clusters = new[]
{
new ClusterConfig
{
ClusterId = "cluster1",
Destinations = { { "destination1", new DestinationConfig { Address = "https://localhost:10000" } } },
HttpClient = new HttpClientConfig
{
MaxConnectionsPerServer = 10,
SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13,
},
},
};
services.AddReverseProxy().LoadFromMemory(routes, clusters);For anything the schema doesn't cover, ConfigureHttpClient exposes the underlying SocketsHttpHandler directly - it runs every time a cluster is added or changed, after the cluster's own settings have already been applied:
C#services.AddReverseProxy()
.ConfigureHttpClient((context, handler) =>
{
handler.SslOptions.ClientCertificates.Add(clientCert);
});Replacing the client factory entirely
For full control, replace IForwarderHttpClientFactory with a custom implementation - deriving from the default ForwarderHttpClientFactory covers most cases. A custom factory should still set the same SocketsHttpHandler properties the default one does, to avoid breaking proxy behavior or adding unnecessary overhead: UseProxy = false, AllowAutoRedirect = false, AutomaticDecompression = DecompressionMethods.None, UseCookies = false.
Always return an HttpMessageInvoker, not an HttpClient - HttpClient buffers responses by default, which breaks streaming and adds latency and memory overhead that plain proxying doesn't need.