I am having problems proxying an ASP.NET Core MVC app.
My app is running on Kestrel on localhost:5000
and my Apache 2.4 reverse proxy is running on localhost:80
. I want to proxy all requests from localhost:80/test
to localhost:5000
Here is the httpd.conf
part for the proxy:
... <Location "/test"> ProxyPass "http://localhost:5000" ProxyPassReverse "http://localhost:5000" </Location> ...
The proxy works, however all links are broken. Like if I have an anchor that links to a controller named HomeController
with the action About
, the link returned from the proxy is localhost/Home/About
instead of localhost/test/Home/About
. The host is correct, it's the context root test
that is missing.
What is the best practice to handle this? Is it a configuration in ASP.NET Core to specify the context root so that TagHelpers will take it into account? Or is it a configuration in Apache ProxyPass
to rewrite links (really not a big fan of this)?
The problem is that the web application does not know about the subpath /test
, so in your case, it will just respond as if it was called directly at the root path.
ASP.NET Core has a concept of a PathBase
to remedy this. The PathBase
is essentially a part of the request’s URI path that will be considered as a fixed part of the path. Whenever a component within the framework generates an URL, it will take the current PathBase
into account and make sure to include that as a prefix to all generated paths.
By default, the PathBase
will be empty, and it depends on the configuration of your reverse proxy to say how you should set up the PathBase
.
There is the built-in UsePathBaseMiddleware which can be used to temporarily configure the PathBase
for an incoming request. The way this middleware works is basically that it will check whether the request starts with an accepted path prefix and if it does, that prefix will be moved from the Path
into the PathBase
.
You can activate this using the UsePathBaseExtensions.UsePathBase
extension method. Just call the method as the very first thing in your Startup’s Configure
method with the base path you want to use:
public void Configure(IApplicationBuilder app) { app.UsePathBase("/test"); // all the other middlewares app.UseStaticFiles(); app.UseMvc(); // … }
Since the middleware will only adjust the PathBase
when it sees the /test
prefix within the path of incoming requests, you need to make sure that the reverse proxy actually includes that prefix. So you would have to adjust your Apache configuration to include that path:
<Location "/test"> ProxyPass "http://localhost:5000/test" ProxyPassReverse "http://localhost:5000/test" </Location>
Note that the UsePathBaseMiddleware will not prevent the application from working without that prefix. So you can actually use it both with and without the base path, and it will correctly adapt.
If you do not want to adjust your reverse proxy configuration to include the path within the forwarded request, then you won’t be able to use the UsePathBaseMiddleware
. Instead, you will have to add your own very simple middleware there:
public void Configure(IApplicationBuilder app) { app.Use((context, next) => { context.Request.PathBase = "/test"; return next(); }); // all the other middlewares app.UseStaticFiles(); app.UseMvc(); // … }
This will set /test
as a constant PathBase
for incoming requests. So the actually incoming request does not have to include it, which means you can leave your reverse proxy configuration as it is (and not forward the /test
path there). However, that also means that unlike with the UsePathBaseMiddleware
where the PathBase
was set dynamically depending on the incoming request, now all requests to the application will require that path, regardless of whether they go through the reverse proxy or not.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With