Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / IIS

Switching Between HTTP and HTTPS Like A Bigshot Hotshot

4.96/5 (27 votes)
22 Feb 2010CPOL4 min read 1   1.1K  
How to automatically enforce and switch between secure (HTTPS/SSL) and non-secure (HTTP/non-SSL) web pages without hard-coding absolute URLs, using SEO friendly redirects.

Introduction

When we, as developers, encounter the same coding scenario time and time again, we naturally tend to encapsulate the coding logic and reuse it in an effort to save time and minimize maintenance.

Recently, while developing a website called Bigshot Hotshot, I reevaluated the need to switch between secure (HTTPS/SSL) and non-secure (HTTP/non-SSL) pages. I noticed that while coding, we do not, in most cases, think about using SSL. One reason is that (at the time of this writing) the ASP.NET Development Server does not support SSL. To test SSL pages, we need to add our application/website to IIS and configure it accordingly. Another problem I wanted to solve was how to hint to IIS that certain pages should always use HTTPS while others should always use HTTP. To complicate things even further, I wanted to take SEO (search engine optimization) into account as well so that redirecting between secure and non-secure pages does not have a negative impact on the website's SEO.

This article presents one way of solving the aforementioned issues. For brevity, we will abbreviate the phrase: switch(ing) between HTTP and HTTPS to HTTP <=> HTTPS.

Background Research

Solution/Proposal 1

While researching potential solutions for the issue of HTTP <=> HTTPS, I came across an article by Matt Sollars: Switching Between HTTP and HTTPS Automatically: Version 2. It is a well-written solution to the above problem. I like how you can enforce entire directories to use SSL, as well as individual pages. I also like the web.config based approach to specify which files should use SSL. On the other hand, the solution is more complicated than what I needed. In addition, HTTP <=> HTTPS by calling Response.Redirect([path], true), and ending the Response is not very SEO friendly.

Solution/Proposal 2

Another solution to HTTP <=> HTTPS I came across was by Yohan B: RequireSSL Attribute for ASP.NET. I like the Attribute based approach of specifying that certain pages are required to use SSL. I also like the use of the #if DEBUG directive to tell the compiler not to HTTP <=> HTTPS while running in Debug mode (since ASP.NET Development Server does not support SSL anyway). What I am not quite fond of, however, is the use of a base Page that all other Pages inherit from to call the Validate() method and control HTTP <=> HTTPS. Also, just like in the above article, HTTP <=> HTTPS by calling Response.Redirect([path], true), and ending the Response is not very SEO friendly.

Our Strategy

What we will be looking at in the next section is another way to HTTP <=> HTTPS. We will use Attributes to mark which Pages require SSL, and we will implement a custom HTTP module responsible for intercepting requests to our ASPX pages and for HTTP <=> HTTPS when necessary. We will also examine how to do this in an SEO friendly manner.

The Code

First off, we need to define an Attribute so we can decorate the Pages that require SSL with that Attribute. Let's define an Attribute called RequireSSL:

C#
/// <summary>
/// Attribute decorated on classes that use SSL
/// <summary>
[AttributeUsage(AttributeTargets.Class)]
sealed public class RequireSSL : Attribute
{
}

In our example project, the login.aspx and signup.aspx pages require SSL. We will mark them accordingly (notice the RequireSSL attribute):

C#
/// <summary>
/// The Login Page
/// </summary>
[RequireSSL]
public partial class login : System.Web.UI.Page
{
    ...
}

/// <summary>
/// The SignUp Page
/// </summary>    
[RequireSSL]
public partial class signup : System.Web.UI.Page
{
    ...
}

Next, we will implement our custom HTTP module. It will be configured so that the code for HTTP <=> HTTPS only runs when compiled in Release mode:

C#
/// <summary>
/// HttpModule for switching between HTTP and HTTPS (HTTP <=> HTTPS)
/// </summary>
public class RequireSSLModule : IHttpModule
{
    public void Init(HttpApplication context)
    {
// only attach the event if the build is not set to Debug
#if !DEBUG
        // The PreRequestHandlerExecute event occurs just
        // before ASP.NET begins executing a handler such as a Page
        // In here we can acquire a reference
        // to the currently executing ASPX Page
        context.PreRequestHandlerExecute += 
           new EventHandler(OnPreRequestHandlerExecute);
#endif
    }
    
    ...
}

Let's take a closer look at the PreRequestHandlerExecute event.

C#
/// <summary>
/// Handle switching between HTTP and HTTPS.
/// It only switches the scheme when necessary.
/// Note: By scheme we mean HTTP scheme or HTTPS scheme.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void OnPreRequestHandlerExecute(object sender, EventArgs e)
{
    // obtain a reference to the ASPX Page 
    System.Web.UI.Page Page = 
       HttpContext.Current.Handler as System.Web.UI.Page;

    // if a valid Page was not found, exit
    if (Page == null)
    {
        return;
    }

    // check if the Page is decorated with the RequireSSL attribute
    bool requireSSL = (Page.GetType().GetCustomAttributes(
                         typeof(RequireSSL), true).Length > 0);

    // check if the Page is currently using the HTTPS scheme
    bool isSecureConnection = 
      HttpContext.Current.ApplicationInstance.Request.IsSecureConnection;

    // acquire the URI (eg~ http://localhost/default.aspx)
    Uri baseUri = HttpContext.Current.ApplicationInstance.Request.Url;

    // if the Page requires SSL and it is not currently
    // using HTTPS, switch out the scheme
    if (requireSSL && !isSecureConnection)
    {
        // switch the HTTP scheme to the HTTPS scheme
        string url = baseUri.ToString().Replace(
                         baseUri.Scheme, Uri.UriSchemeHttps);

        // perform a 301 redirect to the secure url
        PermanentRedirect(url);
    }
    // if the page does not require SSL and it is currently
    // using the HTTPS scheme, switch out the scheme
    else if (!requireSSL && isSecureConnection)
    {
        // switch the HTTPS scheme to the HTTP scheme
        string url = baseUri.ToString().Replace(baseUri.Scheme, 
                                                Uri.UriSchemeHttp);

        // perform a 301 redirect to the non-secure url
        PermanentRedirect(url);
    }
}

I've commented the code above, and it should be fairly straightforward. There are a couple of things I'd like to note:

  1. The first is that the code has been optimized to only HTTP <=> HTTPS when necessary.
  2. Second, the only pages allowed to use SSL are those marked with the RequireSSL module. These will be redirected to HTTPS, while all others will automatically be redirected to use HTTP.
  3. The third thing I'd like to point out is the method PermanentRedirect([url]). This method performs a 301 redirect. This is the most search engine friendly way of redirecting to another Page. The 301 status code means that a Page has permanently moved to a new location. It is implemented as:
C#
private void PermanentRedirect(string url)
{
    HttpContext.Current.Response.Status = "301 Moved Permanently";
    HttpContext.Current.Response.AddHeader("Location", url);
}

In the upcoming ASP.NET 4.0, Microsoft has added PermanentRedirect to the ASP.NET framework itself. You can read more about it here.

The only thing left to do now is to register the RequireSSL module inside the web.config file. Note: You can safely register the module in both places below, so that you needn't worry about whether you are running your website on IIS6 or IIS7.

For IIS6 or IIS7 running in Classic Mode

XML
<configuration>
    <system.web>
        <httpModules>
            <add name="RequireSSL" type="Code.RequireSSLModule, Code" />
        </httpModules>
    </system.web>
</configuration>

For IIS7 running in Integrated Mode:

XML
<configuration>
    <system.webServer>
        <modules>
            <add name="RequireSSL" 
               preCondition="managedHandler" 
               type="Code.RequireSSLModule, Code" />
        </modules>
    </system.webServer>
</configuration>

Running the Code

Running the code from Visual Studio is as easy as pushing F5 on your keyboard. However, if you'd like to see the RequireSSL module in action, you'll need to compile the project in Release mode, configure the website in IIS, create a self-signed SSL certificate for it, and add the HTTPS binding for it. If you need some help setting up SSL in IIS7, you can refer to this article: Enabling SSL on IIS 7.0 Using Self-Signed Certificates.

That's it, and it is how Bigshot Hotshot enforces secure pages and HTTP <=> HTTPS.

Happy coding!

History

  • 22/02/2010: Initial release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)