Skip to main content
SEMastery
DevOpsbeginner

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.

12 min readUpdated January 17, 2026

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.

A client talks only to the gateway, which forwards to the right service

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.

TermWhat it doesExample job
Reverse proxyForwards requests from clients to backend serversSend /orders to the orders server
API gatewayA reverse proxy that also handles shared API jobsCheck login, limit requests, rewrite paths
Backend serviceA small app that does one part of the workThe 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.ReverseProxy

Now 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

Request
Match Route
Pick Cluster
Choose Destination
Forward
Reply

Steps

1

Request

Client hits the gateway

2

Match Route

YARP finds the route by path

3

Pick Cluster

Route points to a ClusterId

4

Choose Destination

Load balancer picks a server

5

Forward

Request sent to backend

6

Reply

Backend answer returned to client

From client request to backend reply, step by step

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.

A route names a cluster, and a cluster names its destinations

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.

PolicyHow it picks a server
PowerOfTwoChoicesLooks at two random servers, picks the less busy one (the default)
RoundRobinGoes in order: server 1, then 2, then 3, then back to 1
LeastRequestsPicks the server handling the fewest requests right now
RandomPicks any server by chance
FirstAlphabeticalAlways 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.

The gateway spreads requests across three identical servers

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

Ping Server
Healthy?
Keep In Pool
Remove From Pool

Steps

1

Ping Server

Send test request to /health

2

Healthy?

Did it answer in time?

3

Keep In Pool

Yes: server gets traffic

4

Remove From Pool

No: skip until it recovers

YARP keeps checking each server and routes only to healthy ones

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.

A full request journey through the gateway and back

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.json so 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 ClusterId and route names like orders-route and orders-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

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 RoundRobin or the default PowerOfTwoChoices.
  • 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