YARP as an API Gateway in .NET: A Beginner's Guide
Learn how to use YARP as an API gateway in .NET 10. Routes, clusters, load balancing, health checks, auth, and transforms explained in simple, friendly steps.
One guard at the front gate
Picture a big school in your town. There is just one main gate at the front. A security guard stands there. When a parent comes to drop a tiffin box, the guard does not let them walk all over the school. The guard checks who they are, then says, "Class 5 is on the second floor, go there," and sends them the right way.
The classrooms behind the gate do not each need their own guard. They trust that anyone who reached them already passed the front gate. The guard handles the checking once, for everyone.
An API gateway works the same way. It is one front door for many small backend services. Clients talk only to the gateway. The gateway checks the request, then forwards it to the correct service. And YARP is the tool that lets you build that front gate in .NET.
In this guide you will learn what YARP is, how routes and clusters work, how to add load balancing and health checks, and how to handle login checks and request changes. We will keep the words simple and the steps small.
What is YARP?
YARP stands for Yet Another Reverse Proxy. It is a free, open-source library from Microsoft. You add it to a normal ASP.NET Core project, give it some configuration, and it becomes a reverse proxy.
A reverse proxy is a server that sits in front of other servers. The client sends a request to the proxy. The proxy passes it on to a backend server, gets the answer back, and returns it to the client. The client never sees the backend directly.
Here is the nice part: YARP is just C# and configuration. You can read the code, debug it, and add your own logic. It supports modern web features like HTTP/2, HTTP/3, gRPC, and WebSockets. Microsoft uses YARP itself to handle billions of requests every day, so it is well tested at scale.
And because YARP is free under the MIT license, you can use it in commercial products without paying. This is worth saying clearly, because some popular .NET libraries such as MediatR and MassTransit have moved to a paid commercial license. YARP did not. It stays free.
Reverse proxy vs API gateway
People sometimes mix up these two words. They are related but not the same. A reverse proxy is the plumbing. An API gateway is a reverse proxy plus extra jobs that help you manage APIs.
| Term | What it does | Example job |
|---|---|---|
| Reverse proxy | Forwards requests from clients to backend servers | Send /orders to the orders server |
| API gateway | A reverse proxy that also handles shared API jobs | Check login, limit requests, rewrite paths |
| Backend service | A small app that does one part of the work | The orders service stores orders |
YARP gives you the reverse proxy. You then add ASP.NET Core features like authentication and rate limiting on top, and together they make a full API gateway.
Setting up your first YARP gateway
Let us build a tiny gateway. First, create a new empty web project and add the YARP package.
dotnet new web -n MyGateway
cd MyGateway
dotnet add package Yarp.ReverseProxyNow open Program.cs. We tell the app to add the reverse proxy and load its settings from configuration. This is the whole gateway in a few lines.
var builder = WebApplication.CreateBuilder(args);
// Add YARP and read routes/clusters from appsettings.json
builder.Services
.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
var app = builder.Build();
// This line wires the proxy into the request pipeline
app.MapReverseProxy();
app.Run();That MapReverseProxy() call is the magic switch. It tells the app: any request that matches a route should be forwarded to the matching cluster. Now we just need to describe those routes and clusters.
Routes and clusters: the heart of YARP
YARP configuration has two main parts. Once you understand these two words, the rest is easy.
- A route describes which requests to catch. Usually you match by path, like anything starting with
/orders. - A cluster describes where to send those requests. It lists one or more destinations, which are backend addresses.
A route points at a cluster by its ClusterId. So the route says what to catch and the cluster says where to forward it.
How a request flows through YARP
Steps
Request
Client hits the gateway
Match Route
YARP finds the route by path
Pick Cluster
Route points to a ClusterId
Choose Destination
Load balancer picks a server
Forward
Request sent to backend
Reply
Backend answer returned to client
Here is a simple appsettings.json. It has one route and one cluster. Any request starting with /products/ goes to the products service.
{
"ReverseProxy": {
"Routes": {
"products-route": {
"ClusterId": "products-cluster",
"Match": {
"Path": "/products/{**catch-all}"
}
}
},
"Clusters": {
"products-cluster": {
"Destinations": {
"d1": {
"Address": "https://localhost:5101/"
}
}
}
}
}
}The piece /products/{**catch-all} means "match /products/ and everything after it". So GET /products/42 and GET /products/search?q=pen both match this route and get forwarded.
Let me show how the two halves connect.
Load balancing across many servers
What if your products service is busy and you run three copies of it? You do not want all traffic hitting one copy. You want the load balanced across them.
In YARP, you just list more destinations in the same cluster, and pick a load balancing policy.
{
"ReverseProxy": {
"Clusters": {
"products-cluster": {
"LoadBalancingPolicy": "RoundRobin",
"Destinations": {
"d1": { "Address": "https://localhost:5101/" },
"d2": { "Address": "https://localhost:5102/" },
"d3": { "Address": "https://localhost:5103/" }
}
}
}
}
}Now requests are spread across the three copies. If you do not set a policy, YARP uses PowerOfTwoChoices, which is a smart default. Here are the common policies and what they mean.
| Policy | How it picks a server |
|---|---|
| PowerOfTwoChoices | Looks at two random servers, picks the less busy one (the default) |
| RoundRobin | Goes in order: server 1, then 2, then 3, then back to 1 |
| LeastRequests | Picks the server handling the fewest requests right now |
| Random | Picks any server by chance |
| FirstAlphabetical | Always picks the first healthy server in order |
For most apps, the default works well. RoundRobin is a good, easy-to-explain choice when all your servers are equally powerful.
Health checks: skip the sick servers
Imagine one of your three product servers crashes. You do not want the gateway to keep sending requests to a dead server. Those requests would just fail.
YARP solves this with health checks. There are two kinds.
- Active health checks: YARP sends a small test request to each server now and then, like every few seconds, to a special health URL. If the server answers nicely, it is healthy. If not, YARP stops sending real traffic to it.
- Passive health checks: YARP watches the real traffic. If a server keeps returning errors, YARP marks it unhealthy and gives it a rest before trying again.
Here is active health checking in configuration. The gateway pings /health on each destination.
{
"ReverseProxy": {
"Clusters": {
"products-cluster": {
"HealthCheck": {
"Active": {
"Enabled": true,
"Interval": "00:00:10",
"Timeout": "00:00:05",
"Policy": "ConsecutiveFailures",
"Path": "/health"
}
},
"Destinations": {
"d1": { "Address": "https://localhost:5101/" },
"d2": { "Address": "https://localhost:5102/" }
}
}
}
}
}With this set, a crashed server is quietly skipped, and your users still get answers from the healthy copies. When the sick server recovers and passes the check again, YARP brings it back into rotation.
Active health check loop
Steps
Ping Server
Send test request to /health
Healthy?
Did it answer in time?
Keep In Pool
Yes: server gets traffic
Remove From Pool
No: skip until it recovers
Adding login checks at the gate
Remember the school guard who checks who you are before letting you in? Your gateway can do the same with authentication and authorization.
The big win is that you check the user once, at the gateway. The backend services trust that anyone who reached them already passed the check. They do not each repeat the login logic.
In ASP.NET Core you add the normal auth services, then attach a policy to a route. Here is the shape in Program.cs.
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
// Normal ASP.NET Core authentication and authorization
builder.Services.AddAuthentication("Bearer").AddJwtBearer();
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapReverseProxy();
app.Run();Then in appsettings.json, a route can name an authorization policy. Only requests that pass the policy are forwarded.
{
"ReverseProxy": {
"Routes": {
"orders-route": {
"ClusterId": "orders-cluster",
"AuthorizationPolicy": "default",
"Match": { "Path": "/orders/{**catch-all}" }
}
}
}
}Now a request to /orders/15 must carry a valid token, or the gateway rejects it before it ever touches the orders service. One guard, many protected rooms.
Transforms: changing requests on the way through
Sometimes the path a client uses is not the path the backend expects. Or you want to add a header so the backend knows the request came through the gateway. Transforms let you change a request before it is forwarded, and change the response before it goes back.
A common example: clients call /api/products, but the products service only knows /products. You strip the /api prefix with a transform.
{
"ReverseProxy": {
"Routes": {
"products-route": {
"ClusterId": "products-cluster",
"Match": { "Path": "/api/products/{**catch-all}" },
"Transforms": [
{ "PathRemovePrefix": "/api" },
{ "RequestHeader": "X-Gateway", "Set": "yarp" }
]
}
}
}
}Here two things happen. First, PathRemovePrefix drops the /api part, so the backend sees /products/.... Second, a header named X-Gateway is added with the value yarp, so the backend can tell the request was forwarded. Transforms are a clean way to keep clients and backends loosely connected.
Putting the whole picture together
Let us step back and see how a single request travels through a full gateway with auth, routing, balancing, and health checks all working together.
This single flow shows why a gateway is so useful. The client made one simple call. Behind the scenes, the gateway did the security check, found the right service, picked a healthy copy, and returned the answer. The client never needed to know any of that.
A few tips before you ship
These small habits will save you trouble later.
- Keep routes in
appsettings.jsonso you can change them without rebuilding the app. YARP reloads config changes for you. - Always add health checks in production. A gateway without health checks will happily forward traffic to a dead server.
- Do auth at the gateway, but still protect sensitive services on their own too, in case someone reaches them directly inside your network.
- Use clear
ClusterIdand route names likeorders-routeandorders-cluster. Future you will thank present you. - Test load balancing by running two copies locally and watching the traffic split. It builds confidence fast.
References and further reading
- Get started with YARP — Microsoft Learn
- YARP Project Home
- dotnet/yarp on GitHub
- Implementing an API Gateway For Microservices With YARP — Milan Jovanović
- YARP as API Gateway in .NET: 7 Real-World Scenarios — Anton DevTips
Quick recap
- YARP (Yet Another Reverse Proxy) is a free, open-source library from Microsoft for building reverse proxies and API gateways in ASP.NET Core.
- An API gateway is one front door for many backend services, like a single guard at a school gate.
- A route says which requests to catch; a cluster says where to forward them. A route points to a cluster by
ClusterId. - A cluster can hold many destinations, and YARP spreads traffic with a load balancing policy such as
RoundRobinor the defaultPowerOfTwoChoices. - Health checks stop the gateway from sending traffic to servers that are down.
- You add authentication and authorization once at the gateway, so every service stays protected.
- Transforms change requests and responses as they pass through, like removing a path prefix or adding a header.
- YARP is free under the MIT license, unlike some libraries such as MediatR and MassTransit that now need a paid license.
Related Posts
Implementing an API Gateway for Microservices With YARP
Learn to build an API gateway for microservices with YARP in .NET 10. Routes, clusters, auth, rate limits, and transforms explained in simple steps.
Horizontally Scaling ASP.NET Core APIs With YARP Load Balancing
Learn how to scale ASP.NET Core APIs horizontally using YARP load balancing, with policies, health checks, and a full Program.cs setup explained simply.
Implementing API Gateway Authentication With YARP in .NET
Learn to build a secure API gateway in .NET using YARP. Add authentication, per-route authorization policies, and pass user identity to backend services.
YARP vs Nginx: A Quick Performance Comparison for .NET
A simple, friendly look at YARP vs Nginx as a reverse proxy: how each one works, real benchmark numbers, tuning tips, and how to pick the right one.
How to Set Up Production-Ready Monitoring With ASP.NET Core Health Checks
A friendly, step-by-step guide to production-ready monitoring with ASP.NET Core health checks: liveness, readiness, dependency checks, a UI, and probes.
Containerize Your .NET Applications Without a Dockerfile
Learn how to build container images for your .NET apps using the SDK and dotnet publish, with no Dockerfile needed. Beginner-friendly guide for .NET 10.