Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hosting behind reverse proxy with subdirectory #2302

Closed
ygoe opened this issue Dec 9, 2017 · 20 comments

Comments

Projects
None yet
6 participants
@ygoe
Copy link

commented Dec 9, 2017

Sorry to use this platform but I need to find another issue and can't find it because GitHub won't let me find issues I'm subscribed to.

I'm hosting an ASP.NET Core application on Linux with Apache web server behind a reverse proxy as recommended. I can setup my environment to only proxy a subdirectory of the domain to the app, but the app doesn't know that because the proxy translates the public subdirectory to the internal root directory. Now there was some discussion going on here about whether to let the hosting configure this (just like the port number of Kestrel web server through an environment variable) and how an application can handle it through code until then.

I need to find that code. I'm sure it was mentioned here, but neither Google nor GitHub won't find it for me. And of course I'm not archiving GitHub notification e-mails that long. Maybe I should do that. Can you please point me to the issue for that?

@Tratcher

This comment has been minimized.

Copy link
Member

commented Dec 10, 2017

Is the request forwarded with the url intact and you want to split it? UsePathBase works for that.

@ygoe

This comment has been minimized.

Copy link
Author

commented Dec 10, 2017

Now I guess somebody needs to tell Kestrel that whatever it sees as / is in fact /app and it should generate URLs to other places accordingly. I'd like this to be configurable from the outside of the ASP.NET Core app because the path is determined by the hosting environment and not by the application itself. This could be through an environment variable. But AFAIR that isn't supported yet and the app has to call some method to add this behaviour.

Adding this code doesn't do anything for me:

app.UsePathBase(new PathString("/subdir"));
@davidfowl

This comment has been minimized.

Copy link
Member

commented Dec 10, 2017

Did you put it first in the pipeline?

@ygoe

This comment has been minimized.

Copy link
Author

commented Dec 10, 2017

Yes, the first instruction in the Startup.Configure method, before UseForwardedHeaders and the env.IsDevelopment() from the MVC template.

@Tratcher

This comment has been minimized.

Copy link
Member

commented Dec 10, 2017

You need the reverse of UsePathBase, you're not trying to trim the path, you just need to set PathBase.

app.Use((context, next) => {
context.Request.PathBase = "/app";
next();
});
@ygoe

This comment has been minimized.

Copy link
Author

commented Dec 10, 2017

@Tratcher Did you mean one of these alternatives?

With missing return:

app.Use((context, next) => {
	context.Request.PathBase = "/subdir";
	return next();
});

And with async/await like in the documentation samples:

app.Use(async (context, next) => {
	context.Request.PathBase = "/subdir";
	return await next();
});

(Edit: No, these are wrong, too. Please correct.)

@Tratcher

This comment has been minimized.

Copy link
Member

commented Dec 10, 2017

Right, coding on my phone...

Note that PathBase is the url root used when generating links, etc.. It is not a file path. That's why I set it to "/app".

What's not working for you?

@ygoe

This comment has been minimized.

Copy link
Author

commented Dec 10, 2017

I've tried this and it works! This is the code I need in apps hosted on my server. And I'd like it to be default for the ASP.NET Core framework.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
	string basePath = Environment.GetEnvironmentVariable("ASPNETCORE_BASEPATH");
	if (!string.IsNullOrEmpty(basePath))
	{
		app.Use(async (context, next) =>
		{
			context.Request.PathBase = basePath;
			await next.Invoke();
		});
	}
	app.UseForwardedHeaders(new ForwardedHeadersOptions
	{
		ForwardedHeaders = ForwardedHeaders.All
	});

	// existing code...
}

My starter script sets these environment variables before running the dotnet command: (This is a simplified sample, the actual script is written in PHP.)

export ASPNETCORE_URLS=http://127.0.0.1:$port
export ASPNETCORE_BASEPATH=$basePath
dotnet $applicationDllFile

And these directives are used for the Apache proxy configuration: (Again, pseudo-variables.)

ProxyPass "/$path" http://127.0.0.1:$port/ retry=15
ProxyPassReverse "/$path" http://127.0.0.1:$port/

So nobody found the original issue on this topic and I haven't received any notifications from it recently, I guess it got lost. So this is now my official request to build this base path support into ASP.NET Core. This allows hosters to map an application into a subdirectory in the URL without modifications to the application code. The environment variable ASPNETCORE_URLS already exists and serves a similar purpose, to make the application server listen on a specific port, again determined by the hoster. The new environment variable ASPNETCORE_BASEPATH would extend this to the URL path.

Maybe the older issue mentioned something like supporting ASPNETCORE_URLS=http://127.0.0.1:$port/$basePath/. But IIRC that wasn't allowed back then. I guess I'd duplicate the base path in the proxy configuration then. I'm not sure if that would serve the same purpose though because the application would probably have to handle the subdirectory more explicitly.

@ygoe

This comment has been minimized.

Copy link
Author

commented Dec 10, 2017

@Tratcher My last reply took too long to write, so here's my answer to your reply.

What's not working for you?

It doesn't compile. The => is underlined because not all code paths return a value. Not sure why that error is reported. See my last reply for the correction I decided to make.

@ygoe

This comment has been minimized.

Copy link
Author

commented Dec 10, 2017

BTW, what happens when you add an ASP.NET (Core) application in a virtual subdirectory in IIS? Will that work without modifications to the app itself? Does the IIS integration handle what I have described here?

@Tratcher

This comment has been minimized.

Copy link
Member

commented Dec 11, 2017

The issue you're looking for is https://github.com/aspnet/Hosting/issues/1120. The main difference is that we expect the forwarded url to contain the path base.

@shanselman

This comment has been minimized.

Copy link
Contributor

commented Jul 2, 2019

Might be worth doing this (reopen) so that we don't need to do this https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-2.2#deal-with-path-base-and-proxies-that-change-the-request-path given the existence of Azure Front Door? @rynowak

@Tratcher Tratcher reopened this Jul 2, 2019

@Tratcher Tratcher added the enhancement label Jul 2, 2019

@Tratcher Tratcher added this to the Backlog milestone Jul 2, 2019

@Tratcher

This comment has been minimized.

Copy link
Member

commented Jul 2, 2019

Re-opening but keeping in backlog. We'll re-triage this for a future release.

Triage note: This is related to but distinct from #5898. The proxy deleted segments from the URL and we need to put them back.

@Tratcher

This comment has been minimized.

Copy link
Member

commented Jul 2, 2019

I take that back, #5898 (comment) already accounts for this.

@Tratcher Tratcher closed this Jul 2, 2019

@shanselman

This comment has been minimized.

Copy link
Contributor

commented Jul 2, 2019

I'm pretty sure this is the inverse of PathBase. I'm having to add it back in order to get ~ to work.

I'm taking http://staging.hanselman.com/blog and pointing it to http://hanselmanblog.azurewebsites.net so I need to add it BACK not strip it. Per https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-2.2#deal-with-path-base-and-proxies-that-change-the-request-path

app.Use((context, next) =>
{
     context.Request.PathBase = new PathString(/blog);
     return next.Invoke();
});
@Eilon

This comment has been minimized.

Copy link
Member

commented Jul 3, 2019

@shanselman / @Tratcher - if this isn't resolved, would it make sense for the two of you to discuss this on a call briefly? Just suggesting 😄

@Tratcher

This comment has been minimized.

Copy link
Member

commented Jul 3, 2019

We've chatted and I've redirected him to #5898. There's several related scenarios we can address together.

@Eilon

This comment has been minimized.

Copy link
Member

commented Jul 3, 2019

Great!

@SheepRock

This comment has been minimized.

Copy link

commented Jul 19, 2019

I've tried this and it works! This is the code I need in apps hosted on my server. And I'd like it to be default for the ASP.NET Core framework.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
	string basePath = Environment.GetEnvironmentVariable("ASPNETCORE_BASEPATH");
	if (!string.IsNullOrEmpty(basePath))
	{
		app.Use(async (context, next) =>
		{
			context.Request.PathBase = basePath;
			await next.Invoke();
		});
	}
	app.UseForwardedHeaders(new ForwardedHeadersOptions
	{
		ForwardedHeaders = ForwardedHeaders.All
	});

	// existing code...
}

My starter script sets these environment variables before running the dotnet command: (This is a simplified sample, the actual script is written in PHP.)

export ASPNETCORE_URLS=http://127.0.0.1:$port
export ASPNETCORE_BASEPATH=$basePath
dotnet $applicationDllFile

And these directives are used for the Apache proxy configuration: (Again, pseudo-variables.)

ProxyPass "/$path" http://127.0.0.1:$port/ retry=15
ProxyPassReverse "/$path" http://127.0.0.1:$port/

So nobody found the original issue on this topic and I haven't received any notifications from it recently, I guess it got lost. So this is now my official request to build this base path support into ASP.NET Core. This allows hosters to map an application into a subdirectory in the URL without modifications to the application code. The environment variable ASPNETCORE_URLS already exists and serves a similar purpose, to make the application server listen on a specific port, again determined by the hoster. The new environment variable ASPNETCORE_BASEPATH would extend this to the URL path.

Maybe the older issue mentioned something like supporting ASPNETCORE_URLS=http://127.0.0.1:$port/$basePath/. But IIRC that wasn't allowed back then. I guess I'd duplicate the base path in the proxy configuration then. I'm not sure if that would serve the same purpose though because the application would probably have to handle the subdirectory more explicitly.

For me it worked for almost everything, but when an unlogged user tries to access an page with the [Authorize] decorator, the basepath is being add twice. I have

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme,
    options =>
    {
        options.LoginPath = "/Account/Login";
        options.LogoutPath = "/Account/Logout";
    });

but during the navigation, the user gets http://server/basepath/basepath/Account/Login

Anyone have this issue? Is it a bug or configuration?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.