Wed, 10 Aug 2011

Redirecting to HTTPS in Windows Azure: Two Methods

Using HTTPS is a good idea. It means communication between the browser and your web application is encrypted and thus safe from eavesdropping. The only reason I rarely use HTTPS in my applications is that I don’t want to buy an SSL certificate.

If you do provide HTTPS support for your application in Windows Azure, you may want to redirect users to the HTTPS version (even if they came in via HTTP). In this blog post, I’ll show you two ways to accomplish this redirect. You can test the second method at http://forcehttps.cloudapp.net. (Note that I’m using a self-signed certificate, so you’ll have to click through a browser warning.)

ASP.NET MVC RequireHttps Attribute

Since version 2, ASP.NET MVC includes a RequireHttps attribute that can be used to decorate a controller or a specific action. If you’re using ASP.NET MVC 3, this is by far the simplest way to force an HTTPS redirect. Usage is trivial:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    [RequireHttps]
    public ActionResult Secure()
    {
        return View();
    }
}

This will automatically redirect users to the HTTPS version of your site when they try to browse to the “Secure” controller action. Note that the redirect will always use port 443 (the default HTTPS port), so if you’re testing locally under the compute emulator and you’re getting an HTTPS port other than 443 (because, for example, that port is already taken by another web site), this won’t work well. I don’t know of a good workaround for this. Just make sure port 443 is available.

IIS URL Rewrite

If you’re not using ASP.NET MVC but are using IIS (as in a web role), you can use the URL Rewrite module, which is installed by default. Using this module to redirect to HTTPS is fairly trivial and documented elsewhere, but getting everything to work properly in the local compute emulator is non-trivial. In particular, most examples you’ll find assume that HTTP traffic will always be on port 80, which isn’t always the case when testing under the compute emulator. Here’s a rule that seems to work locally and in the cloud:

<system.webServer>
  <rewrite>
    <rules>
      <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>

I’ve put in two exceptions (the negate="true" lines). The first is for the root of the domain. Here I just wanted to show how to add an exception for certain URLs (like maybe a login screen). If you’re entire site supports HTTPS, there’s little reason for this. The second exception is for actual files (as opposed to controller actions). This allows JavaScript and CSS files to be loaded even over HTTP, making the first page work.

The standard rule you’ll see uses something like url="https://{HTTP_HOST}/{R:1}". The reason I didn’t do that is because of local ports in the compute emulator. {HTTP_HOST} actually includes the port, so this will result in rewriting to URLs like https://127.0.0.1:82:443/…, which won’t work at all. (Note the double port.)

[UPDATE 10:30am PDT] I had a more complicated server rule here, but @tpettijohn pointed me to his post about HTTPS rewriting that had a better rule using {SERVER_NAME}, a variable I wasn’t aware of.

The trick I used is to apply a regular expression to the {HTTP_HOST} variable and extract just the host name itself (without the port). This is then captured and available in the {C:1} variable in the rewrite URL. Doing it this way mimics the behavior you see from the RequireHttps attribute in ASP.NET MVC, where the port is stripped and HTTPS traffic always goes to port 443 (the default).

The above URL Rewrite rule is what’s in use at http://forcehttps.cloudapp.net.

[UPDATE 10am PDT] It may be obvious to some, but I do want to point out that using either of the above methods in Windows Azure means you’ll need to first declare an HTTPS endpoint and supply a certificate (configured locally by thumbprint and uploaded via the Windows Azure portal to make available in the cloud). See the MSDN topic “How to Configure an SSL Certificate on an HTTPS Endpoint.”