YARP – A fast and reliable reverse proxy

Possibilities of the .NET-based open source solution from Microsoft

Routing, load balancing, authentication, authorisation and reliability are important issues in many web projects. Microsoft has a large number of teams that write a reverse proxy for their services themselves or look for solutions to map the tasks mentioned above. This was a good occasion to bring together the different requirements to work on a common solution. YARP was launched – the Microsoft open source project for a reverse proxy in .NET.

Microsoft released the first preview more than a year ago. Ever since, many improvements have been made and YARP has been released in version 1.0 together with the new .NET 6 on 9 November 2021.

In this article we aim to take a closer look at YARP and provide an overview of the configuration options provided to us by Microsoft. Accordingly, we will start by looking at what a reverse proxy actually is and how YARP is positioned. Then we will look at the diverse configuration options and conclude with an outlook.

YARP is written in C# and is built on .NET. It utilises the infrastructure of ASP.NET and .NET. Thus, .NET Core 3.1 and .NET 5 as well as the .NET 6 mentioned above are supported. However, when .NET Core 3.1 is used, some functions are not available, as YARP makes use of some new features and optimisations in the newer .NET versions.

keyboard with open source button

What is a reverse proxy?

A reverse proxy is a type of proxy server that is typically located behind the firewall in a private network and forwards client requests to back-end services. In doing so, the reverse proxy provides an additional abstraction and control layer to ensure the smooth flow of network traffic between clients and servers.

What is YARP?

A classic reverse proxy usually operates on the transport layer (4th layer – TCP/IP) of the ISO/OSI model and routes the requests further and further. In contrast, YARP resides on the 7th layer – here the http layer – and it cuts the incoming connections and creates new ones to the target server. The incoming and outgoing connections are thus independent. This enables remapping of the URL space, i.e. there is a difference between the URLs that are visible from outside and those in the back-end.

The back-end server can be relieved of load by shifting tasks to the reverse proxy.

Why YARP?

The utilisation options of YARP and the many other (classic) reverse proxies vary. A developer in the ASP.NET environment can easily set up, configure and extend the functionality of the reverse proxy in his or her usual programming language. Likewise, the reverse proxy with all its configurations can be versioned with version control, just like any other project. It also has cross-platform capability, i.e. it runs on both Windows and Linux, making it well suited for containerisation.

Functions of YARP

One of the most important requirements is the extensibility and customisability of YARP. For configuration, any source that maps the IConfiguration interface can be connected. Classically, this would be JSON configuration files. The configuration is automatically updated without a restart when changes are made. However, it is also possible to control the configuration dynamically via an API or even on demand per request.

Anatomy of a request

To better understand the functions, it is useful to first get an overview of the pipeline architecture of YARP. Initially, an incoming request lands in the standard ASP .NET middleware (e.g. TLS Termination, Static Files, Routing, Authentication and Authorisation). This is followed by various phases of YARP.

  1. Run through all target servers incl. health check
  2. Session affinity
  3. Load balancing
  4. Passive health checks
  5. Transformation of the request
  6. Forwarding of the request to the target server through a new connection

Routes and clusters

The reverse proxy can be configured for both routes and clusters. The route configuration is an ordered list of route hits with their associated configurations. A route is typically defined by three components: the route ID, cluster ID and a match criterion. Therefore, when an incoming connection arrives, it is compared with the match criterion. In the process, the list of route entries is processed one after the other. If the match criterion is met, the cluster with the specified ID is used for forwarding. However, CORS, transformer, authentication and authorisation can also be configured for a route.

Figure 1 shows an example configuration for routes and clusters.

Unlike the “Routes section, the Clusters section contains an unordered collection of named clusters. A cluster primarily contains a collection of named targets and their addresses, each of which is considered capable of handling requests for a particular route. The proxy processes the request according to the route and cluster configuration to select a target.

{
    "ReverseProxy": {
        "Routes": {
            "minimumroute": {
                "ClusterId": "minimumcluster",
                "Match": {
                    "Path": "{**catch-all}"
                }
            },
            "route2": {
                "ClusterId": "cluster2",
                "Match": {
                    "Path": "/something/{*any}"
                }
            }
        },
        "Clusters": {
            "minimumcluster": {
                "Destinations": {
                    "example.com": {
                        "Address": "http://www.example.com/"
                    }
                }
            },
            "cluster2": {
                "Destinations": {
                    "first_destination": {
                        "Address": "https://contoso.com"
                    },
                    "another_destination": {
                        "Address": "https://bing.com"
                    }
                },
                "LoadBalancingPolicy": "PowerOfTwoChoices"
            }
        }
    }
}

Figure 1: Example configuration with the basic functions (routes, clusters and load balancing)

TLS termination

As mentioned earlier, incoming connections are cut and new ones to the target server are established. Since TLS connections are expensive, this can improve speed for small requests. This can be useful especially if the proxy forwards to servers that are all on the internal network and a secure connection is no longer necessary. The following possibilities are conceivable here:

  • Routing of an incoming HTTPS to HTTP connection
  • Routing of an incoming HTTPS/1 to HTTP/2 connection
  • Routing of an incoming HTTP to HTTPS connection

Session affinity

Session affinity is a mechanism for binding (affinity) a contiguous request sequence to the target that handled the first request when the load is distributed over multiple targets.

It is useful in scenarios where most requests in a sequence deal with the same data and the cost of accessing data is different for different targets handling requests.

The most common example is transient caching (e.g. in-memory). Here, during the first request, data is retrieved from a slower persistent memory into a fast local cache. Subsequent requests can then be processed with the data from the cache, thus increasing throughput.

Load balancing

If multiple healthy targets are available for a route, one of the following load balancing algorithms can be configured:

  • Random
  • Round robin
  • Least requests
  • Power of two choices
  • First

It is also possible to use a self-developed algorithm at this point.

Health checks

The reverse proxy can analyse the health of each node and stop client traffic to unhealthy nodes until they recover. YARP implements this approach in the form of active and passive checks.

Passive

YARP can passively check for successes and failures in forwarding client requests. Responses to proxy requests are intercepted by dedicated passive health checking middleware, which forwards them to a policy configured on the cluster. The policy analyses the responses to determine whether or not the targets that generated them are healthy. It then computes new passive health states, assigns them to the respective targets and rebuilds the cluster’s collection of healthy targets.

Active

YARP can also actively monitor the health of the target servers. This is done by regularly sending requests to predefined state endpoints. This analysis is defined by an active health check policy set for a cluster. At the end, the policy is used to mark each target as healthy or unhealthy.

Unhealthy clusters are automatically blocked through the active and passive checks, and maintenance can be performed without adversely affecting the application.

Transformers

Transformers allow the proxy to modify parts of the request or response. This may be necessary, for example, to meet defined requirements of the target server. The original request object is not changed, only the proxy request. No analysis of the request body is done and no modification of the request and response body takes place. However, this can be achieved via additional middleware – if necessary. It is at this point, for example, where the API gateway Ocelot, which is also based on .NET, could show its strengths. It can perform conversions such as XML to JSON or merge several responses and is primarily aimed at .NET applications with a microservice or service-oriented architecture.

There are some transformers that are enabled by default. These include the protocol (X-Forwarded-Proto), the requested server (X-Forwarded-Host) and the origin IP (X-Forwarded-For).

Accordingly, the origin of the request is still known after the reverse proxy is routed in the back-end via the added header information – this is not the case with classic reverse proxies.

Authentication and authorisation

Authentication and authorisation are possible before forwarding. This allows consistent policies to be mapped across multiple services, eliminating the need to maintain the policies separately. In addition, it leads to a load reduction for the target systems. The authorisation policies are an ASP.NET Core concept. One policy is set per route and the rest is handled by the existing ASP.NET Core authentication and authorisation components.

The following procedures are supported by YARP:

  • Cookie, Bearer, API Keys
  • OAuth2, OpenIdConnect, WsFederation
  • Client certificates

Windows, Negotiate, NTLM and Kerberos authentication, on the other hand, are not supported, as these are usually bound to a specific connection.

Cross-Origin Resource Sharing – CORS

YARP can handle cross-origin requests before they are routed to the target server. This reduces the load on the target servers and ensures consistent policies.

Direct forwarding with IHttpForwarder

If the application does not require the full range of functions, just the IHttpForwarder can be used instead of the full feature set. It serves as a proxy adapter between incoming and outgoing connections.

In this case, the proxy takes over the creation of an HttpRequestMessage from an HttpContext, the sending and forwarding of the response.

The IHttpForwarder supports dynamic target selection, where one defines the target for each request.

Adjustments can be made to the request and response, where the body is excluded. And last but not least, the streaming protocols gRPC and WebSockets as well as error handling are supported.

This minimalistic version does not support routing, load balancing, session affinity and retries – but it does provide some performance benefits.

Outlook

For future releases, the team behind YARP is working on support for HTTP/3, Service Fabric, integration in Kubernetes and further performance improvements.

In addition, Microsoft is trying to develop LLHTTP (Low Level HTTP), an alternative to the current HttpClient, in order to have more control over how requests are made and processed. It is intended to be used especially in scenarios where performance is more important than ease of use. In YARP, it is to be used to gain more control over outgoing connections and for more efficient processing of headers.

Summary

The present article explained the basics of YARP and its extensive features. Using the knowledge gained and the multitude of good code examples in the YARP repository on GitHub, you can now assess whether the functionalities are sufficient for a given use case and create your own reverse proxy.

Since the toolkit is based on the ASP.NET Core stack, it can be run on any environment you have been using for your .NET Core projects.

With YARP, Microsoft delivers a fast and reliable next-generation reverse proxy that will be applied in many projects – not only those of Microsoft.

This post was written by: