9

I have two IIS websites set up on the same IIS server. Website A contains most of the content and this is what users are shown when requesting our address (e.g. www.websitea.com). Website B is a separate project that contains just a part of the whole content, so it is internal (bound in IIS to websiteb.com), but through a URL Rewrite, users can get to it by typing www.websitea.com/websiteb.

The URL rewrite looks like this in website A's web.config:

<rewrite>
  <rules>
    <clear />
    <rule name="Website B rewrite rule" stopProcessing="true">
      <match url="^websiteb(.*)" />
      <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
        <add input="{CACHE_URL}" pattern="^(https?)://" />
      </conditions>
      <action type="Rewrite" url="{C:1}://websiteb.com{R:1}" />
    </rule>
  </rules>
</rewrite>

The {CACHE_URL} and {C:1} bits are to keep the protocol used. E.g. a user requesting www.websitea.com/websiteb/foo.html on HTTP gets "rewritten" to websiteb.com/foo.html on HTTP, and a request to websitea.com/websiteb/bar.html on HTTPS gets "rewritten" to websiteb.com/bar.html on HTTPS.

Now, for some Website B pages we want the user to use HTTPS only - this is set in our SomePageViewModel's ShouldBeHttps property. So the following code is used in an ActionFilterAttribute:

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsSecureConnection)
            return;

        var result = filterContext.Result as ViewResult;
        if (result != null)
        {
            if ((result.Model as SomePageViewModel).ShouldBeHttps)
            {
                HandleNonHttpsRequest(filterContext);
            }
        }
    }

    protected virtual void HandleNonHttpsRequest(ActionExecutedContext filterContext)
    {
        if (!string.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
            throw new InvalidOperationException("The method must be a GET");
        string url = filterContext.HttpContext.Request.Url.ToString().Replace("http:", "https:");
        filterContext.Result = new RedirectResult(url);
    }

Let's say www.websitea.com/websiteb/securepage.html will result in a true value in the ShouldBeHttps property.

Now, when I test it directly on the server by going to websiteb.com/securepage.html on HTTP, I get correctly redirected (status code 302) to websiteb.com/securepage.html on HTTPS.

I would expect that when I go to www.websitea.com/websiteb/securepage.html on HTTP, I will be redirected to www.websitea.com/websiteb/securepage.html on HTTPS. However, my browsers end up in a redirect loop (ERR_TOO_MANY_REDIRECTS). I can see in Fiddler in TextView tab that it seems to be correctly set up:

<html>
 <head>
  <title>Object moved</title>
 </head>
 <body>
 <h2>Object moved to <a href="https://www.websitea.com/websiteb/securepage.html">here</a>.</h2>
 </body>
</html>

But the Headers tab shows:

Response Headers
HTTP/1.1 302 Found
(...)
Transport
  Location: http://www.websitea.com/websiteb/securepage.html

So instead of going to https, it is again http and that hits the filter again, and so on.

Is there anything I'm missing? Is it some IIS setting?

patrykgliwinski
  • 316
  • 1
  • 8
  • I think the problem is the rewrite rule doesn't see the difference between an incoming http or https call, so it will rewrite both back to https. – Quintium Feb 17 '16 at 15:53
  • I believe it works OK, I can see requests to HTTPS www.websitea.com/websiteb/index.html correctly going to HTTPS websiteb.com/index.html and HTTP to HTTP; IsSecureConnection property on the HttpContext.Current.Request is set to true/false correctly. – patrykgliwinski Feb 17 '16 at 16:08
  • Ah. I see. I misread that part. But if you want websitea to redirect to websiteb in the rewrite rule, shouldn't be instead? – Quintium Feb 17 '16 at 16:55
  • Nope. This sits in web.config for websitea, and the translates roughly to _"for requests received by this websitea when their url path (i.e. the bit after websitea.com/) starts with websiteb"_, and then ends the sentence with _"return the markup served by websiteb.com"_. There may be something else still missing, though :( Thanks for trying! – patrykgliwinski Feb 17 '16 at 17:39

2 Answers2

-1

Try this config. Works for my website

<rule name="Redirect to HTTPS" stopProcessing="true">
<match url="(.*)" />
<conditions><add input="{HTTPS}" pattern="^OFF$" />
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="SeeOther" />
</rule>
Michael Samteladze
  • 1,310
  • 15
  • 38
  • No, no, I need to use that ShouldBeHttps value, which is "calculated" server-side only after the rewrite is done; so I cannot "hard-code" https in the rewrite action. – patrykgliwinski Feb 22 '16 at 09:40
-1

This is mostly a bug in IIS

1.Disable the URL Rewrite Cache

2.Add an "unsafe" variable to your rule

3.Clear your cache

And best way, if you can do it in code without using Rewrite

Depending on what version you using of IIS, this could fix your bug:

x64

x86

KB2974666