woutervs
woutervs

Reputation: 1510

URL Redirection failing

So situation sketch:

I've created a website in asp.net core, i've got it up and running. A couple of days ago my hosting provider added the option to configure SSL so I did that leaving me with the problem that there was no redirection from http to https. I've started looking on the web for solutions and figured out some code that works in 3 of the 4 cases on the hosting and 4 of the 4 cases when tested locally on IIS.

So what works:

The following code was used in the asp.net core site:

public void Configure(...) {
  app.UseHttpsWorldWideWebRedirect();
}

public static class HttpsWorldWideWebRedirectMiddlewareExtensions
    {
        public static IApplicationBuilder UseHttpsWorldWideWebRedirect(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<HttpsWorldWideWebRedirectMiddleware>();
        }
    }

public class HttpsWorldWideWebRedirectMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<HttpsWorldWideWebRedirectMiddleware> _logger;

    public HttpsWorldWideWebRedirectMiddleware(RequestDelegate next, ILogger<HttpsWorldWideWebRedirectMiddleware> logger)
    {
        _next = next;
        _logger = logger;
        _logger.LogInformation("Https WWW redirect module loaded");
    }

    public async Task Invoke(HttpContext context)
    {
        var protoHeader = context.Request.Headers["X-Forwarded-Proto"].ToString();
        var ignoreHttps = false;
        var ignoreWww = false;

        if (context.Request.IsHttps || protoHeader.ToLower().Equals("https"))

        {
            ignoreHttps = true;
        }

        if (context.Request.Host.Host.StartsWith("www", StringComparison.OrdinalIgnoreCase) || string.Equals(context.Request.Host.Host, "localhost", StringComparison.OrdinalIgnoreCase))
        {
            ignoreWww = true;
        }

        if (ignoreWww && ignoreHttps)
        {
            await _next.Invoke(context);
        }
        else
        {
            var www = ignoreWww ? "" : "www.";
            var newPath =
                $"https://{www}{context.Request.Host.Value}{context.Request.PathBase}{context.Request.Path}{context.Request.QueryString}";
            _logger.LogDebug($"'{context.Request.GetDisplayUrl()}' was rewritten into '{newPath}'");
            context.Response.Headers["X-Forwarded-Proto"] = "https";
            context.Response.Redirect(newPath, true);
        }
    }
}

The next step I tried was adding a redirect in the web.config to cover that one case where asp.net redirection not works using the following code:

<rewrite>
  <rules>
    <rule name="Redirect to https if http://www" stopProcessing="true">
      <match url="(.*)" />
      <conditions logicalGrouping="MatchAny">
        <add input="{HTTP_HOST}" pattern="^www.*"/>
        <add input="{HTTPS}" pattern="Off" />
      </conditions>
      <action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="Permanent" />
    </rule>
  </rules>
</rewrite>

As a test I added "iwork" tot this line:

The result still is err too many redirection but then with http://someurl.com/iworkiworkiworkiworkiworkiworkiworkiworkiworkiwork as resulting url.

So I'm at a loss for what exactly is going wrong and out of ideas on how to fix it, any ideas?

Upvotes: 3

Views: 772

Answers (3)

Kul-Tigin
Kul-Tigin

Reputation: 16950

Don't know anything about .NET Core but this can certainly be done with URL rewrite rules.

You need at least 2 rewrite rules.

  1. Redirect http requests to https with www.
  2. Redirect https request with no www. prefix to https with www.

This will reduce the redirect chain as much as possible.


<rewrite>
    <rules>
        <rule name="Redirect all http requests to https with www" stopProcessing="true">
            <match url=".*" />
            <conditions trackAllCaptures="true">
                <add input="{HTTP_HOST}" pattern="^localhost" negate="true" />
                <add input="{HTTPS}" pattern="off" />
                <add input="{HTTP_HOST}" pattern="^(?:www\.)?(.+)$" />
            </conditions>
            <action type="Redirect" url="https://www.{C:1}/{R:0}" />
        </rule>
        <rule name="Redirect https requests with no www prefix to https with www" stopProcessing="true">
            <match url=".*" />
            <conditions>
                <add input="{HTTP_HOST}" pattern="^localhost" negate="true" />
                <add input="{HTTPS}" pattern="on" />
                <add input="{HTTP_HOST}" pattern="^www\." negate="true" />
            </conditions>
            <action type="Redirect" url="https://www.{HTTP_HOST}/{R:0}" />
        </rule>
    </rules>
</rewrite>

Upvotes: 4

Graham
Graham

Reputation: 7802

We just faced something very similar in our Azure configuration, and in the end the best way was to use the rewrite module in web.config and not try to handle this in the code.

This is very close to what we added to our server, it should work for you with some minor edits:

<system.webServer>
  <rewrite>
    <rules>
      <rule name="Add WWW">
        <match url="^(.*)$" />
        <conditions>
          <add input="{HTTP_HOST}" pattern="^(?!www\.)(.*)$" />
        </conditions>
        <action type="Redirect" url="www.{C:0}{PATH_INFO}" redirectType="Permanent" />
       </rule>
      <rule name="Redirect to HTTPS">
        <match url="(.*)" />
        <conditions>
          <add input="{HTTPS}" pattern="off" ignoreCase="true" />
          <add input="{URL}" pattern="/$" negate="true" />
          <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
        </conditions>
        <action type="Redirect" url="https://{SERVER_NAME}/{R:1}" redirectType="SeeOther" />
      </rule>
    </rules>
  </rewrite>
</system.webServer>

Here are two sites that have helpful information:

https://learn.microsoft.com/en-us/iis/extensions/url-rewrite-module/using-the-url-rewrite-module

http://blog.smarx.com/posts/redirecting-to-https-in-windows-azure-two-methods

Upvotes: 1

Omar Beyhum
Omar Beyhum

Reputation: 557

There is existing Middleware that will handle redirecting to HTTPS for you automatically. Just add the following at the top of your Configure() method in your Startup class:

var options = new RewriteOptions().AddRedirectToHttps();
app.UseRewriter(options);

If you're using APIs, you could also add a RequireHttpsAttribute filter to your MVC Options in ConfigureServices():

services.AddMvc(mvcOptions => 
        {
            mvcOptions.Filters.Add(new RequireHttpsAttribute());

        });

Upvotes: 2

Related Questions