Use YARP to expose multiple local ASP.NET applications under friendly hostnames instead of localhost ports.
For example, your solution might contain several applications running on different ports:
https://localhost:37101https://localhost:37102https://localhost:37103
Instead of accessing each application by port, you can expose them through local hostnames:
https://localhost:37101→https://cm.dev.localhosthttps://localhost:37102→https://cd1.dev.localhosthttps://localhost:37103→https://cd2.dev.localhost
Starting with .NET 10, the ASP.NET Core development certificate includes support for *.dev.localhost. This means any hostname ending with .dev.localhost is trusted by the default development certificate after it has been installed and trusted on your machine.
This allows you to use friendly HTTPS hostnames such as https://cm.dev.localhost without creating custom certificates or configuring a local certificate authority.
Ensure the ASP.NET Core development certificate is installed and trusted:
dotnet dev-certs https --trustThis only needs to be done once per machine.
Domains ending with .localhost normally resolve to the local loopback address automatically, so this step is often unnecessary. If your environment requires it, add the following entries to your hosts file (C:\Windows\System32\drivers\etc\hosts):
127.0.0.1 cm.dev.localhost
127.0.0.1 cd1.dev.localhost
127.0.0.1 cd2.dev.localhost
Create a new empty ASP.NET project and install YARP:
dotnet package add Yarp.ReverseProxyConfigure the reverse proxy to listen on HTTPS port 443 (https://localhost:443) and forward incoming requests to your local applications.
The example includes the following files:
ReverseProxy.Program.csReverseProxy.launchSettings.jsonReverseProxy.appsettings.json
Each application behind the proxy must be configured to process forwarded headers so it can determine the original host and HTTPS scheme.
The example includes:
AspNetSites.Program.cs
Your local applications can now be accessed using friendly hostnames instead of localhost ports.
For example, if these were separate Umbraco instances:
https://cm.dev.localhost/umbraco/→https://localhost:37101/umbraco/https://cd1.dev.localhost/page-1/→https://localhost:37102/page-1/https://cd2.dev.localhost/page-2/→https://localhost:37103/page-2/
The request flow looks like this:
flowchart TD
A["Browser<br/>https://cm.dev.localhost/page-1"]
B["YARP Reverse Proxy<br/>HTTPS :443"]
C{"Match Host header"}
D["cm.dev.localhost<br/>→ https://localhost:37101"]
E["cd1.dev.localhost<br/>→ https://localhost:37102"]
F["cd2.dev.localhost<br/>→ https://localhost:37103"]
A --> B
B --> C
C --> D
C --> E
C --> F
YARP accepts the incoming request on port 443, matches the request hostname (cm.dev.localhost) against its configured routes, and forwards the request to the appropriate local application while preserving the original host and protocol through forwarded headers.
If you're using this setup with Umbraco, you should also configure the public URLs that Umbraco should use when generating links and handling authentication.
{
"Umbraco": {
"CMS": {
"Security": {
"BackOfficeHost": "https://cm.dev.localhost"
},
"WebRouting": {
"UmbracoApplicationUrl": "https://cd1.dev.localhost/"
}
}
}
}BackOfficeHost tells Umbraco which public URL hosts the backoffice.
This is especially important when using a reverse proxy, as developers often access the site through https://cm.dev.localhost, while YARP forwards requests internally to something like https://localhost:37101.
Without this setting, authentication can fail with errors such as:
error: invalid_request
error_description: The specified 'redirect_uri' is not valid for this client application.
UmbracoApplicationUrl defines the public URL that Umbraco uses when generating absolute URLs, redirects, canonical links, and other application URLs.
When running behind a reverse proxy, this should point to the public hostname that users access—not the internal localhost address.
Also see: UmbracoApplicationUrl
A common setup is:
| Purpose | Public URL | Internal URL |
|---|---|---|
| Backoffice | https://cm.dev.localhost |
https://localhost:37101 |
| Website 1 | https://cd1.dev.localhost |
https://localhost:37102 |
| Website 2 | https://cd2.dev.localhost |
https://localhost:37103 |
YARP handles routing from the public hostnames to the internal applications, while Umbraco continues to generate URLs and authentication callbacks using the public hostnames.
If you'd like to learn more about YARP and ASP.NET reverse proxies, these resources are a great place to start:
-
YARP official website
https://dotnet.github.io/yarp/
Project overview, quick start, samples, and documentation. :contentReference[oaicite:0]{index=0} -
YARP GitHub repository
https://github.com/dotnet/yarp
Source code, issues, discussions, and release notes. :contentReference[oaicite:1]{index=1} -
Configuration Files
https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/config-files
Learn how routes, clusters, destinations, and configuration reloading work. :contentReference[oaicite:2]{index=2} -
HTTPS & TLS
https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/https-tls
Understand TLS termination, HTTPS forwarding, backend certificates, and SNI. :contentReference[oaicite:3]{index=3} -
Request & Response Transforms
https://learn.microsoft.com/aspnet/core/fundamentals/servers/yarp/transforms
Customize forwarded headers, paths, hosts, query strings, and responses. :contentReference[oaicite:4]{index=4}