
Azure App Service replaced Nginx with YARP, gaining over 80% improvement in throughput. They process more than 160B requests daily (1.9M RPS). This is a remarkable technological innovation from Microsoft.
First, let us introduce what YARP is.
What is YARP?
YARP (Yet Another Reverse Proxy) is an open-source, high-performance reverse proxy library developed by Microsoft and written in C#. It is designed as a foundation for building reverse proxy servers on the .NET platform. YARP targets .NET 5 and above, allowing developers to easily implement reverse proxy functionality within .NET applications.
Key Features and Capabilities of YARP:
- Modularity and Extensibility: YARP is designed to be highly modular, meaning internal components such as HTTP request routing, load balancing, and health checks can be swapped or extended as needed.
- Performance: YARP is optimized for high performance, leveraging .NET's asynchronous programming model and efficient I/O operations to handle a large number of concurrent connections.
- Configuration-Driven: YARP's behavior can be controlled through configuration, supporting dynamic loading from files, databases, or other sources.
- Routing: Request routing rules can be configured based on various parameters such as path, headers, and query parameters.
- Load Balancing: Includes multiple built-in load balancing strategies like round-robin, least connections, random selection, and supports custom load balancing strategies.
- Health Checks: Supports health checks for backend services to ensure requests are only forwarded to healthy service instances.
- Transformers: Allows transformation of requests and responses, such as modifying headers, paths, or query parameters.
- Session Affinity: Supports session affinity, ensuring requests from the same client are sent to the same backend service instance.
Common Use Cases for YARP:
- Reverse Proxy: Provides an intermediate layer between clients and backend services for request forwarding and load balancing.
- API Gateway: Acts as an API gateway in a microservices architecture, providing routing, authentication, monitoring, and more.
- Edge Service: Provides a security layer between the application and the outside world, handling SSL termination, request throttling, etc.
Simple Usage of YARP
Create a WebApi project.
Install the NuGet package:
<ItemGroup>
<PackageReference Include="Yarp.ReverseProxy" Version="2.0.0" />
</ItemGroup>
Open appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"ReverseProxy": {
"Routes": {
"route1": {
"ClusterId": "cluster1",
"Match": {
"Path": "{**catch-all}"
}
}
},
"Clusters": {
"cluster1": {
"Destinations": {
"destination1": {
"Address": "https://cn.bing.com/"
}
}
}
}
}
}
Open Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
var app = builder.Build();
app.MapReverseProxy();
app.Run();
Then start the project. Visiting our API will proxy the request to Bing.

Using YARP in Middleware
Below we provide another way to use YARP in middleware.
We need to use IHttpForwarder.
First modify Program.cs. Here we inject HttpForwarder and provide a Run middleware where we manually specify the endpoint address https://cn.bing.com/. Then we start the project.
using Yarp.ReverseProxy.Forwarder;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpForwarder(); // Inject IHttpForwarder
var app = builder.Build();
var httpMessage = new HttpMessageInvoker(new HttpClientHandler());
app.Run((async context =>
{
var httpForwarder = context.RequestServices.GetRequiredService<IHttpForwarder>();
var destinationPrefix = "https://cn.bing.com/";
await httpForwarder.SendAsync(context, destinationPrefix, httpMessage);
}));
app.Run();
It will also be proxied, but unlike the simple usage, here we control the proxy at the code level.

Modifying Bing's Response Content with YARP
We continue based on the above proxy usage to modify Bing's response content.
Open Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpForwarder(); // Inject IHttpForwarder
var app = builder.Build();
var httpMessage = new HttpMessageInvoker(new HttpClientHandler()
{
// 忽略https错误
ServerCertificateCustomValidationCallback = (_, _, _, _) => true,
AllowAutoRedirect = false,
AutomaticDecompression = DecompressionMethods.GZip,
UseCookies = false,
UseProxy = false,
UseDefaultCredentials = true,
});
var destinationPrefix = "https://cn.bing.com/";
var bingTransformer = new BingTransformer();
app.Run((async context =>
{
var httpForwarder = context.RequestServices.GetRequiredService<IHttpForwarder>();
await httpForwarder.SendAsync(context, destinationPrefix, httpMessage, new ForwarderRequestConfig(),
bingTransformer);
}));
app.Run();
Create BingTransformer.cs:
public class BingTransformer : HttpTransformer
{
public override async ValueTask TransformRequestAsync(HttpContext httpContext, HttpRequestMessage proxyRequest,
string destinationPrefix,
CancellationToken cancellationToken)
{
var uri = RequestUtilities.MakeDestinationAddress(destinationPrefix, httpContext.Request.Path,
httpContext.Request.QueryString);
proxyRequest.RequestUri = uri;
proxyRequest.Headers.Host = uri.Host;
await base.TransformRequestAsync(httpContext, proxyRequest, destinationPrefix, cancellationToken);
}
public override async ValueTask<bool> TransformResponseAsync(HttpContext httpContext,
HttpResponseMessage? proxyResponse,
CancellationToken cancellationToken)
{
await base.TransformResponseAsync(httpContext, proxyResponse, cancellationToken);
if (httpContext.Request.Method == "GET" &&
httpContext.Response.Headers["Content-Type"].Any(x => x.StartsWith("text/html")))
{
var encoding = proxyResponse.Content.Headers.FirstOrDefault(x => x.Key == "Content-Encoding").Value;
if (encoding?.FirstOrDefault() == "gzip")
{
var content = proxyResponse?.Content.ReadAsByteArrayAsync(cancellationToken).Result;
if (content != null)
{
var result = Encoding.UTF8.GetString(GZipDecompressByte(content));
result = result.Replace("国内版", "Token Bing 搜索 - 国内版");
proxyResponse.Content = new StringContent(GZipDecompressString(result));
}
}
else if (encoding.FirstOrDefault() == "br")
{
var content = proxyResponse?.Content.ReadAsByteArrayAsync(cancellationToken).Result;
if (content != null)
{
var result = Encoding.UTF8.GetString(BrDecompress(content));
result = result.Replace("国内版", "Token Bing 搜索 - 国内版");
proxyResponse.Content = new ByteArrayContent(BrCompress(result));
}
}
else
{
var content = proxyResponse?.Content.ReadAsStringAsync(cancellationToken).Result;
if (content != null)
{
content = content.Replace("国内版", "Token Bing 搜索 - 国内版");
proxyResponse.Content = new StringContent(content);
}
}
}
return true;
}
/// <summary>
/// 解压GZip
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
public static byte[] GZipDecompressByte(byte[] bytes)
{
using var targetStream = new MemoryStream();
using var compressStream = new MemoryStream(bytes);
using var zipStream = new GZipStream(compressStream, CompressionMode.Decompress);
using (var decompressionStream = new GZipStream(compressStream, CompressionMode.Decompress))
{
decompressionStream.CopyTo(targetStream);
}
return targetStream.ToArray();
}
/// <summary>
/// 解压GZip
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public static string GZipDecompressString(string str)
{
using var compressStream = new MemoryStream(Encoding.UTF8.GetBytes(str));
using var zipStream = new GZipStream(compressStream, CompressionMode.Decompress);
using var resultStream = new StreamReader(new MemoryStream(compressStream.ToArray()));
return resultStream.ReadToEnd();
}
/// <summary>
/// Br压缩
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static byte[] BrCompress(string str)
{
using var outputStream = new MemoryStream();
using (var compressionStream = new BrotliStream(outputStream, CompressionMode.Compress))
{
compressionStream.Write(Encoding.UTF8.GetBytes(str));
}
return outputStream.ToArray();
}
/// <summary>
/// Br解压
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static byte[] BrDecompress(byte[] input)
{
using (var inputStream = new MemoryStream(input))
using (var outputStream = new MemoryStream())
using (var decompressionStream = new BrotliStream(inputStream, CompressionMode.Decompress))
{
decompressionStream.CopyTo(outputStream);
return outputStream.ToArray();
}
}
}
The resulting effect: we changed 国内版 to Token Bing 搜索 - 国内版.

YARP Related Resources
Technical exchange group: 737776595
Official documentation: https://microsoft.github.io/reverse-proxy/articles/getting-started.html
Shared by token