A customizable IP filtering middleware for Microsoft's YARP (Yet Another Reverse Proxy) that provides fine-grained control over allowed or blocked IP addresses globally and per-route, ensuring secure and flexible access management.
Features
- Global IP filtering — a single policy evaluated for every proxied request before route-specific rules
- Route-specific policies — attach a named policy to any YARP route via route metadata
- Allowlist and blocklist modes — allow only known IPs, or block known-bad IPs
- IP address and CIDR network matching — mix individual addresses and network ranges in one policy
- Dynamic reload — configuration changes are picked up at runtime without a restart
- Fast lookups —
HashSetfor addresses and a trie-based collection for networks on the critical path - Built-in tracing — an
ActivitySourceemits a span per evaluation
Installation
Install the package via NuGet:
dotnet add package BBelius.Yarp.ReverseProxy.IPFilters
The package targets .NET 8 and .NET 9 and depends on Yarp.ReverseProxy 2.x.
Quick Start
1. Register the policy provider
Add the IP filter policy provider to your DI container, binding it from configuration:
using BBelius.Yarp.ReverseProxy.IPFilters;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
builder.Services.AddIPFilterPolicies(builder.Configuration);
2. Add the middleware to the YARP pipeline
Register the middleware inside MapReverseProxy. Add it before any other YARP middleware so blocked requests are rejected as early as possible.
var app = builder.Build();
app.MapReverseProxy(proxyPipeline =>
{
proxyPipeline.UseIPFilterPolicies();
proxyPipeline.UseLoadBalancing();
});
app.Run();
Important: Add the middleware to YARP's middleware pipeline via
UseIPFilterPolicies()— not to the regular ASP.NET Core request pipeline. It requires access to the YARPHttpContextroute feature, which is only available insideMapReverseProxy. See the YARP middleware documentation.
3. Configure policies
Define your policies in appsettings.json:
{
"IPFilterConfiguration": {
"EnableGlobalPolicy": true,
"GlobalPolicyName": "Global",
"Policies": [
{
"PolicyName": "Global",
"Mode": "BlockList",
"IPAddresses": ["192.168.0.3", "192.168.0.4"]
},
{
"PolicyName": "Intranet",
"Mode": "AllowList",
"IPNetworks": ["192.168.0.0/24"]
}
]
}
}
4. Attach policies to routes
Reference a policy by name in a route's Metadata:
{
"ReverseProxy": {
"Routes": [
{
"RouteId": "route1",
"ClusterId": "cluster1",
"Match": { "Path": "/{**catch-all}" },
"Metadata": { "IPFilterPolicy": "Intranet" }
}
]
}
}
Documentation
| Topic | Description | ||
|---|---|---|---|
| Configuration | Service registration and the IPFilterConfiguration section | ||
| Policies | Policy modes, IP address and CIDR network matching | ||
| Route Configuration | Attaching policies to YARP routes via metadata | ||
| Global Policy | The global policy evaluated for every request | ||
| Tracing | The ActivitySource and emitted span tags | ||
| Advanced | Programmatic configuration, dynamic reload, custom providers |
How it works
For each proxied request the middleware:
- Resolves the client IP from
HttpContext.Connection.RemoteIpAddress(IPv4-mapped IPv6 addresses are
normalised back to IPv4).
- If a global policy is enabled and the IP is not allowed by it, responds 403 Forbidden
and stops.
- Reads the
IPFilterPolicykey from the current route's metadata. If absent, the request passes straight through. - Looks up the named policy. A
Disabledpolicy passes through; otherwise the IP is matched
against the policy's addresses and networks in the configured mode.
- Allowed requests continue down the pipeline; blocked requests get 403 Forbidden.
A referenced-but-missing policy is a configuration error: the middleware logs a critical event, responds 500 Internal Server Error, and throws IPFilterPolicyNotFoundException.
License
Licensed under the MIT License. Contributions are welcome — open an issue or submit a pull request on GitHub.