YA-RP-UI 0.4.1

dotnet add package YA-RP-UI --version 0.4.1
                    
NuGet\Install-Package YA-RP-UI -Version 0.4.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="YA-RP-UI" Version="0.4.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="YA-RP-UI" Version="0.4.1" />
                    
Directory.Packages.props
<PackageReference Include="YA-RP-UI" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add YA-RP-UI --version 0.4.1
                    
#r "nuget: YA-RP-UI, 0.4.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package YA-RP-UI@0.4.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=YA-RP-UI&version=0.4.1
                    
Install as a Cake Addin
#tool nuget:?package=YA-RP-UI&version=0.4.1
                    
Install as a Cake Tool

YARP UI

A management UI for YARP (Yet Another Reverse Proxy). A single app that is both a working reverse proxy and its control room:

  • Route Map (/) — every route → cluster → destination rendered as an interactive graph. Click a node to trace its full chain and inspect its configuration; search to highlight matches.
  • Editor (/editor) — create, edit and delete routes, clusters and destinations. Saving validates the configuration, applies it to the running proxy without a restart, and persists it to disk.
  • Logs (/logs) — proxied requests (method, path, status, duration, client IP, route, cluster, chosen destination), newest first with sortable columns. The table loads just the latest 10 entries and pages through the rest; all filters (route/cluster/destination, time frame, free text, status class) and sorting search the whole retained history server-side, not just what is loaded, and new entries stream onto the first page live. Plus a performance panel: per-request durations charted over time and colored by status class, avg/P95/max/error-rate stat cards, and per-route aggregates. Each row's client IP carries a one-click block action.
  • IP Blocking (/ipblocking) — block a client IP, CIDR network or from–to range. Blocked requests are rejected with 403 before they reach the proxy (or anything else the host serves). Rules apply immediately, are persisted across restarts, and blocked hits show up in the Logs page like any other request.

Editions — this repository is the community edition, free under Apache-2.0. A separate premium edition adds commercial features on top and is distributed under a commercial license. The premium code never lives in this repository.

Hosting modes

The UI ships as a Razor Class Library (YA-RP-UI NuGet package) and can be hosted two ways:

1. Standalone executableYARPUI.Host is a thin host that runs the proxy and the management UI in a single app:

cd YARPUI.Host && dotnet run      # → http://localhost:5080

2. Embedded in your own app — add the package and wire it up (see samples/EmbeddedHost):

<PackageReference Include="YA-RP-UI" Version="0.4.1" />
var builder = WebApplication.CreateBuilder(args);
builder.AddYarpUi();               // proxy config, services, auth, Razor Pages

var app = builder.Build();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseYarpUiRequestLogging();     // records proxied requests for the Logs page
app.MapYarpUi();                   // the UI pages + /api/yarp/*
app.MapReverseProxy();             // the proxy itself (public)
app.Run();

3. Attached to an app that already configures YARP — for gateways with their own LoadFromConfig/custom providers, transforms and filters. The UI shows the app's entire live configuration and can edit it: saving writes each change back into the appsettings.json file the route or cluster came from, and YARP hot-reloads the file — edits go live without a restart while the app's code (transforms, middleware, custom pipeline) stays untouched:

builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"))
    .AddTransforms(...);            // your custom work stays fully in charge

builder.AttachYarpUi();            // no proxy registration, no config seeding

app.UseStaticFiles();
app.UseAuthentication();
app.UseAuthorization();
app.UseYarpUiRequestLogging();
app.MapReverseProxy();
app.MapYarpUi();

How write-back editing behaves:

  • Edits are merged into the existing JSON nodes — fields the editor doesn't model (e.g. RateLimiterPolicy or custom keys) keep their values; unrelated content in the file is preserved.
  • New routes/clusters are added to appsettings.json; deleted ones are removed from every appsettings file that defines them (including environment overrides).
  • Backups: the first time the UI modifies a file, a .yarpui.bak copy is kept next to it; Restore appsettings backup rolls every modified file back.
  • Items that come from a non-file source (a custom IProxyConfigProvider backed by a database, code, etc.) are shown locked and read-only — there is no file to write back to.
  • Pre-existing config quirks (e.g. a route referencing a missing cluster) don't block saves; only problems the edit itself introduces are rejected.

All modes read the same configuration (YarpUi:Auth credentials) and support YarpUi:DataDirectory for volume-backed persistence. The UI authenticates with its own cookie scheme (YarpUi.Auth) and never changes the host's default authentication scheme, so it is safe next to an app's existing JWT/cookie setup.

Quick start

dotnet run

Open http://localhost:5080 and sign in. Default credentials (change them!):

Setting Value
Username admin
Password yarp-admin

Both are configured in appsettings.json under YarpUi:Auth.

Docker

A template docker-compose.yml ships next to the solution:

docker compose up -d --build

The UI is then served on http://localhost:8090. All mutable configuration is volume-persisted in ./docker-data so it survives docker compose down:

File Purpose
docker-data/appsettings.json Credentials (YarpUi:Auth) and the seed ReverseProxy config — edit on the host, applies on next start
docker-data/yarp-ui.routes.json Written automatically on every save from the UI editor
docker-data/yarp-ui-ipblocklist.json IP block list (rules + settings) — written on every change from the IP Blocking page
docker-data/yarp-ui-logs.db Request log database (SQLite) — survives restarts, purged by the retention policy

Under the hood the container sets YarpUi__DataDirectory=/app/data and mounts the volume there; an appsettings.json in that directory overrides the one baked into the image (this also works without Docker — point YarpUi:DataDirectory anywhere you like). To build the image manually: docker build -t yarp-ui:0.4.1 . from the solution root.

IIS

Hosting under IIS works with the default application pool identity (ApplicationPoolIdentity), which has read-only access to the site folder. On startup YARP UI detects that the content root is not writable and stores all mutable state — yarp-ui-logs.db, yarp-ui.routes.json, the optional data-directory appsettings.json — under %ProgramData%\YarpUi\<application name> instead, logging a warning so the relocation is visible. Nothing needs to be configured; the proxy starts normally and the editor/logs pages work against the fallback folder.

To keep state in a location of your choosing instead, either point YarpUi:DataDirectory at a writable folder, or grant the pool identity write access to the site folder:

icacls "<site folder>" /grant "IIS AppPool\<YourAppPool>:(OI)(CI)(M)"

An explicitly configured YarpUi:DataDirectory is never overridden by the fallback. The fallback location itself can be redirected with YarpUi:FallbackDataDirectory.

How configuration works

appsettings.json ("ReverseProxy" section)   ← hand-written seed
                │
                ▼  startup
   yarp-ui.routes.json (if present)         ← takes precedence once it exists
                │
                ▼
   InMemoryConfigProvider (live YARP config)
  • On startup the app loads yarp-ui.routes.json if it exists; otherwise it reads the ReverseProxy section from appsettings.json.
  • The first Save in the editor writes the full configuration to yarp-ui.routes.json. From that point on, that file is the source of truth — appsettings.json is left untouched.
  • Reset to appsettings.json (editor, bottom-left) deletes the UI-managed file and returns to the seed configuration.
  • Saves are validated with YARP's own config validator; invalid configurations are rejected and the proxy keeps running with the last good config.

Request logs

Only proxied requests are recorded (UI/API requests are excluded). Entries are stored in a SQLite database (yarp-ui-logs.db in the data directory, next to yarp-ui.routes.json) and survive restarts. Each entry captures the method, path, status code, duration, the route/cluster/destination YARP selected, and the client IP. Databases created by older versions are migrated in place on first start.

The client IP is the leftmost X-Forwarded-For entry when a fronting proxy supplied one, otherwise the direct connection address; when YarpUi:ForwardedHeaders is enabled, the address resolved from the trusted front's header is logged instead (see Forwarded headers). Since X-Forwarded-For is caller-controlled, treat logged IPs as informational rather than authenticated, unless forwarded headers are enabled from a front clients cannot bypass.

The Logs page loads only the latest page of entries (10 rows, newest first) and pages through the rest on demand, so opening it stays fast no matter how much history is retained. All filtering and sorting run server-side over the entire retained history via GET /api/yarp/logs with from/to (Unix milliseconds), routeId, clusterId, destinationId, q (free text over path, method, route/cluster/destination and client IP), status (status class 2–5), sort, desc, limit (max 1000 per query) and offset for paging; the response reports the total match count. Without search parameters the endpoint keeps its live-tailing contract: after=<seq> streams new entries oldest-first. The page keeps the first page fresh live (new entries appear at the top while Live is on); deeper pages stay stable while you browse them.

A retention policy deletes logs automatically once they pass a certain age: a background task runs at startup and then every hour. The policy is managed from the Logs page toolbar (Keep logs: forever / 1 / 7 / 30 / 90 / 365 days) and changing it applies immediately; the initial default comes from YarpUi:Logs:RetentionDays in configuration (30 days if unset). The policy you set in the UI is stored in the database itself and wins over the configuration value.

IP blocking

The IP Blocking page (/ipblocking) blocks abusive clients at the front door. YARP itself has no client-IP access control, so YARP UI adds it: a middleware that rejects blocked addresses with 403 before the request reaches routing, the proxy or anything else the host serves. It is enabled in every hosting mode (standalone, embedded and attach) without any host code change — the package inserts it the same way it inserts its localization middleware. With an empty list it costs effectively nothing; with rules loaded, matching is a precompiled hash lookup / binary search with no locks or allocations per request, and adding or removing a rule swaps the compiled list atomically (no restart, no dropped requests).

Rules accept three notations and apply to both IPv4 and IPv6:

Notation Example
Single address 203.0.113.7
CIDR network (host bits must be zero) 203.0.113.0/24
Inclusive from–to range 203.0.113.5-203.0.113.99

Behavior details:

  • What gets blocked: every request except the management UI itself — its pages, /api/yarp/* and its static assets stay reachable no matter what, so an admin can never lock themselves out; a too-wide rule is always removable from the UI (or by deleting yarp-ui-ipblocklist.json in the data directory). In attach mode the block also covers the host application's own routes, since the check runs before routing.
  • Which address is matched: the direct connection address (Connection.RemoteIpAddress), which is unspoofable and correct when YARP UI is the edge proxy. If the whole app sits behind another trusted proxy or load balancer, enable Honor X-Forwarded-For on the page to match the leftmost X-Forwarded-For entry instead. That header is caller-controlled: only enable the toggle when direct clients cannot reach the app, otherwise an attacker can spoof the header to evade (or trigger) blocks. With YarpUi:ForwardedHeaders enabled, Connection.RemoteIpAddress is already the visitor's resolved address, so rules match the real client with the toggle off — see Forwarded headers.
  • Persistence: rules and the toggle live in yarp-ui-ipblocklist.json in the data directory (next to yarp-ui.routes.json), written atomically on every change and reloaded on restart. A corrupt file never takes the app down — it falls back to an empty list with a warning; individual rules that no longer parse are skipped.
  • Visibility: every blocked request is written to the request log (status 403, the matching rule named in the error field, the client IP), so blocks are searchable on the Logs page like any other traffic. The Logs page also has a one-click block button on each row's client IP.
  • API: GET /api/yarp/ipblocking, POST /api/yarp/ipblocking/rules, DELETE /api/yarp/ipblocking/rules/{id}, PUT /api/yarp/ipblocking/settings, and POST /api/yarp/ipblocking/check (reports which rule an address would hit — the page's Test an address box).
  • The list is capped at 1000 rules; overlapping ranges are merged internally (the request is blocked either way, the log names one of the matching rules).

Forwarded headers (the real client IP behind a proxy)

When the whole app sits behind a trusted front — a Cloudflare tunnel, nginx, another load balancer — every request's direct connection address is the front's, not the visitor's: IP blocking sees one address for all traffic and the request log shows the proxy's IP. The opt-in YarpUi:ForwardedHeaders section enables ASP.NET Core's forwarded-headers middleware for you, in every hosting mode, with no host code:

"YarpUi": {
  "ForwardedHeaders": {
    "Enabled": true,
    "ForwardedForHeaderName": "CF-Connecting-IP"
  }
}
Setting Default Meaning
Enabled false Installs the middleware; nothing changes until this is true.
ForwardedForHeaderName X-Forwarded-For The header your front carries the client IP in when it isn't the standard one — CF-Connecting-IP (Cloudflare), True-Client-IP (Akamai). Leave unset for fronts that speak X-Forwarded-For.
KnownProxies (loopback only) Extra peer IP addresses whose forwarded values are trusted.
KnownNetworks (loopback only) Extra trusted peer networks in CIDR notation (172.18.0.0/16), e.g. the docker network a containerized front connects from.
TrustAllProxies false Clears the known-proxy check entirely — only for setups where clients cannot reach the app except through the front (the normal tunnel deployment: no inbound ports open).

What it does:

  • Connection.RemoteIpAddress becomes the visitor's address, resolved from the configured header; X-Forwarded-Proto is honored too, so TLS-terminated fronts produce the right scheme.
  • IP blocking then matches the real visitor with its default settings — no need for the Honor X-Forwarded-For toggle, which reads the spoofable standard chain.
  • The request log records the resolved address, which wins over the leftmost X-Forwarded-For entry — a visitor can spoof that chain by sending their own header, but not the front's header.
  • Loopback is trusted out of the box, so a tunnel process (e.g. cloudflared) on the same machine needs only Enabled plus the header name; a front connecting from a container network additionally needs its range in KnownNetworks (or TrustAllProxies when the app is unreachable except through the front).
  • An unparseable KnownProxies/KnownNetworks value fails startup with an error naming it — a typo'd trust range should be loud, not silently ignored.
  • If the host already installs its own forwarded-headers middleware (UseForwardedHeaders), leave this section off.

Forwarding the client IP to your destinations: headers from the front (including CF-Connecting-IP) already pass through to destinations unchanged. To also send the resolved visitor IP as the standard X-Forwarded-For, add transforms on the route — in the editor's Transforms box:

[
  { "X-ForwardedFor": "Set" },
  { "X-ForwardedProto": "Set" }
]

Set writes a single clean value (the resolved client IP); Append keeps the incoming chain instead.

Localization

The UI ships in English (default), Arabic (rendered right-to-left), Spanish and Simplified Chinese. A request's culture is resolved in this order: the ?culture= query string, the standard ASP.NET Core culture cookie, the browser's Accept-Language header, then the default. The language switcher in the top bar (and on the login page) writes that cookie and reloads.

No host wiring is required: the package inserts its own request-localization middleware scoped to the UI's routes only (/login, the UI pages, /api/yarp/*), so host applications never need to call UseRequestLocalization and their own pages keep whatever culture behavior they had.

Two settings control the language set (in appsettings.json):

Setting Default Meaning
YarpUi:Cultures en,ar,es,zh-Hans,zh-CN Comma-separated cultures the UI may respond in
YarpUi:DefaultCulture en Culture used when a request doesn't match any supported one

zh-CN is accepted as an alias for zh-Hans (browsers send the regional tag); unsupported cultures fall back to the default. Validation errors — both from the management API and from the editor's configuration checks — are localized with the same request culture.

Offline / no network

All JavaScript libraries (Cytoscape.js, dagre, cytoscape-dagre, Chart.js) are vendored under wwwroot/lib/. No CDN is used at runtime; the UI works fully offline.

Security notes

  • The management UI requires sign-in (cookie auth). The proxy routes themselves are public — that's the point of a proxy. Use the IP Blocking page to reject abusive clients (see above); the UI surface itself is deliberately exempt from the block list.
  • Credentials sit in plain text in appsettings.json, which is fine for a local/internal tool. If you expose this app beyond localhost, put it behind HTTPS, use strong credentials, and consider extending the auth with hashed passwords or a real identity provider.
  • Serve over HTTP only on a trusted network; the cookie is not marked Secure so it also works on plain HTTP during development.

License

Copyright 2026 The YARP UI Authors.

Licensed under the Apache License, Version 2.0. This is the community edition of YARP UI; the premium edition is licensed separately and distributed from its own repository.

"YARP UI" and the YARP UI logo are project trademarks; this license does not grant rights to use them to market derivative products.

Bundled third-party libraries (YARP, Microsoft.Data.Sqlite, Cytoscape.js, dagre, cytoscape-dagre, Chart.js) are MIT-licensed — see THIRD-PARTY-NOTICES.md.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.4.1 63 9/4/2026
0.4.0 59 9/4/2026
0.3.1 92 8/21/2026
0.3.0 91 8/21/2026
0.2.1 99 8/20/2026
0.2.0 89 8/20/2026
0.1.2 87 8/19/2026
0.1.1 92 8/18/2026

0.4.1: forwarded headers — opt-in YarpUi:ForwardedHeaders section resolves the real visitor IP when the whole app sits behind a trusted front (a Cloudflare tunnel, nginx, another load balancer) in every hosting mode with no host code: ASP.NET Core's forwarded-headers middleware is installed ahead of IP blocking, configurable to read the front's client-IP header (Cloudflare's CF-Connecting-IP, Akamai's True-Client-IP, or the standard X-Forwarded-For) and honoring X-Forwarded-Proto so TLS-terminated fronts produce the right scheme; trusted peers are configured via KnownProxies/KnownNetworks (loopback is trusted by default, covering a same-machine tunnel process) or TrustAllProxies for deployments reachable only through the front, and invalid entries fail startup with an error naming the value; IP blocking then matches the real visitor with its default settings instead of the front's address (no need for the Honor X-Forwarded-For toggle, which reads the spoofable standard chain); the request log records the resolved address, which wins over the leftmost X-Forwarded-For entry a visitor can spoof by sending their own header; headers from the front (including CF-Connecting-IP) keep passing through to destinations unchanged, and the README documents the X-ForwardedFor: Set route transform for sending the resolved client IP downstream. 0.4.0: IP blocking — new IP Blocking page (/ipblocking) blocks abusive clients by single address, CIDR network or from–to range (IPv4 and IPv6); a middleware rejects blocked requests with 403 before they reach routing or the proxy, enabled in every hosting mode with no host code changes; the management UI itself is always exempt so an admin cannot lock themselves out; rules apply immediately without restarts and persist to yarp-ui-ipblocklist.json; blocked requests are written to the request log and searchable on the Logs page, which also gains a one-click block action per row; optional Honor X-Forwarded-For toggle for deployments behind a trusted proxy; rules, settings and an address tester are managed from the page or via /api/yarp/ipblocking. Logs page scales to large histories: it loads only the latest page and pages on demand, free-text and status-class filters moved server-side over the entire retained history (GET /api/yarp/logs gains q, status, offset and reports the total match count), while live updates keep streaming onto the first page. Published load-test results from the new NBomber harness (boots the real app on in-process Kestrel against a Testcontainers whoami upstream): 13,363 req/s through the proxy at 50 concurrent sessions with zero errors across ~9 million requests — see the Performance section and docs/load-tests/. 0.3.1: Adding translations [Arabic, Spanish, Chinese] 0.3.0: request logs become searchable — new Logs-page filters by time frame (presets or a custom range), route, cluster and destination run server-side over the entire retained history; entries are listed newest first by default and every column is sortable; each entry now records the client IP (leftmost X-Forwarded-For when a proxy supplied it, otherwise the direct connection); GET /api/yarp/logs accepts from/to, routeId, clusterId, destinationId, sort, desc and limit; existing log databases gain the client_ip column via an in-place migration. 0.2.0: request logs are persisted to SQLite (yarp-ui-logs.db) instead of an in-memory buffer, with a UI-managed retention policy enforced by an automated hourly purge task (config seed: YarpUi:Logs:RetentionDays); the Logs page gains a performance panel — per-request durations charted over time and colored by status class, avg/P95/P99/max/error-rate stat cards, and per-route aggregates; attach mode now edits the app's own configuration — changes are written back into the appsettings.json files the routes/clusters came from (hot-reloaded by YARP, with .yarpui.bak backups and restore), items only stay read-only when they come from non-file sources. 0.1.2: fix CSS/JS 404s when consuming the package from NuGet — static assets are now pinned to _content/YARPUI instead of following the package id. 0.1.1: attach mode (overlay). 0.1.0: route map, editor, request logs, auth, standalone + embedded hosting.