[c#] Best way in asp.net to force https for an entire site?

About 6 months ago I rolled out a site where every request needed to be over https. The only way at the time I could find to ensure that every request to a page was over https was to check it in the page load event. If the request was not over http I would response.redirect("https://example.com")

Is there a better way -- ideally some setting in the web.config?

This question is related to c# asp.net vb.net webforms https

The answer is


-> Simply ADD [RequireHttps] on top of the public class HomeController : Controller.

-> And add GlobalFilters.Filters.Add(new RequireHttpsAttribute()); in 'protected void Application_Start()' method in Global.asax.cs file.

Which forces your entire application to HTTPS.


I spent sometime looking for best practice that make sense and found the following which worked perfected for me. I hope this will save you sometime.

Using Config file (for example an asp.net website) https://blogs.msdn.microsoft.com/kaushal/2013/05/22/http-to-https-redirects-on-iis-7-x-and-higher/

or on your own server https://www.sslshopper.com/iis7-redirect-http-to-https.html

[SHORT ANSWER] Simply The code below goes inside

<system.webServer> 
 <rewrite>
     <rules>
       <rule name="HTTP/S to HTTPS Redirect" enabled="true" 
           stopProcessing="true">
       <match url="(.*)" />
        <conditions logicalGrouping="MatchAny">
        <add input="{SERVER_PORT_SECURE}" pattern="^0$" />
       </conditions>
       <action type="Redirect" url="https://{HTTP_HOST}{REQUEST_URI}" 
        redirectType="Permanent" />
        </rule>
       </rules>
 </rewrite>

The other thing you can do is use HSTS by returning the "Strict-Transport-Security" header to the browser. The browser has to support this (and at present, it's primarily Chrome and Firefox that do), but it means that once set, the browser won't make requests to the site over HTTP and will instead translate them to HTTPS requests before issuing them. Try this in combination with a redirect from HTTP:

protected void Application_BeginRequest(Object sender, EventArgs e)
{
  switch (Request.Url.Scheme)
  {
    case "https":
      Response.AddHeader("Strict-Transport-Security", "max-age=300");
      break;
    case "http":
      var path = "https://" + Request.Url.Host + Request.Url.PathAndQuery;
      Response.Status = "301 Moved Permanently";
      Response.AddHeader("Location", path);
      break;
  }
}

Browsers that aren't HSTS aware will just ignore the header but will still get caught by the switch statement and sent over to HTTPS.


What you need to do is :

1) Add a key inside of web.config, depending upon the production or stage server like below

<add key="HttpsServer" value="stage"/>
             or
<add key="HttpsServer" value="prod"/>

2) Inside your Global.asax file add below method.

void Application_BeginRequest(Object sender, EventArgs e)
{
    //if (ConfigurationManager.AppSettings["HttpsServer"].ToString() == "prod")
    if (ConfigurationManager.AppSettings["HttpsServer"].ToString() == "stage")
    {
        if (!HttpContext.Current.Request.IsSecureConnection)
        {
            if (!Request.Url.GetLeftPart(UriPartial.Authority).Contains("www"))
            {
                HttpContext.Current.Response.Redirect(
                    Request.Url.GetLeftPart(UriPartial.Authority).Replace("http://", "https://www."), true);
            }
            else
            {
                HttpContext.Current.Response.Redirect(
                    Request.Url.GetLeftPart(UriPartial.Authority).Replace("http://", "https://"), true);
            }
        }
    }
}

For those using ASP.NET MVC. You can use the following to force SSL/TLS over HTTPS over the whole site in two ways:

The Hard Way

1 - Add the RequireHttpsAttribute to the global filters:

GlobalFilters.Filters.Add(new RequireHttpsAttribute());

2 - Force Anti-Forgery tokens to use SSL/TLS:

AntiForgeryConfig.RequireSsl = true;

3 - Require Cookies to require HTTPS by default by changing the Web.config file:

<system.web>
    <httpCookies httpOnlyCookies="true" requireSSL="true" />
</system.web>

4 - Use the NWebSec.Owin NuGet package and add the following line of code to enable Strict Transport Security accross the site. Don't forget to add the Preload directive below and submit your site to the HSTS Preload site. More information here and here. Note that if you are not using OWIN, there is a Web.config method you can read up on on the NWebSec site.

// app is your OWIN IAppBuilder app in Startup.cs
app.UseHsts(options => options.MaxAge(days: 30).Preload());

5 - Use the NWebSec.Owin NuGet package and add the following line of code to enable Public Key Pinning (HPKP) across the site. More information here and here.

// app is your OWIN IAppBuilder app in Startup.cs
app.UseHpkp(options => options
    .Sha256Pins(
        "Base64 encoded SHA-256 hash of your first certificate e.g. cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2+soZS7sWs=",
        "Base64 encoded SHA-256 hash of your second backup certificate e.g. M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE=")
    .MaxAge(days: 30));

6 - Include the https scheme in any URL's used. Content Security Policy (CSP) HTTP header and Subresource Integrity (SRI) do not play nice when you imit the scheme in some browsers. It is better to be explicit about HTTPS. e.g.

<script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.4/bootstrap.min.js"></script>

The Easy Way

Use the ASP.NET MVC Boilerplate Visual Studio project template to generate a project with all of this and much more built in. You can also view the code on GitHub.


I'm going to throw my two cents in. IF you have access to IIS server side, then you can force HTTPS by use of the protocol bindings. For example, you have a website called Blah. In IIS you'd setup two sites: Blah, and Blah (Redirect). For Blah only configure the HTTPS binding (and FTP if you need to, make sure to force it over a secure connection as well). For Blah (Redirect) only configure the HTTP binding. Lastly, in the HTTP Redirect section for Blah (Redirect) make sure to set a 301 redirect to https://blah.com, with exact destination enabled. Make sure that each site in IIS is pointing to it's own root folder otherwise the Web.config will get all screwed up. Also make sure to have HSTS configured on your HTTPSed site so that subsequent requests by the browser are always forced to HTTPS and no redirects occur.


If you are unable to set this up in IIS for whatever reason, I'd make an HTTP module that does the redirect for you:

using System;
using System.Web;

namespace HttpsOnly
{
    /// <summary>
    /// Redirects the Request to HTTPS if it comes in on an insecure channel.
    /// </summary>
    public class HttpsOnlyModule : IHttpModule
    {
        public void Init(HttpApplication app)
        {
            // Note we cannot trust IsSecureConnection when 
            // in a webfarm, because usually only the load balancer 
            // will come in on a secure port the request will be then 
            // internally redirected to local machine on a specified port.

            // Move this to a config file, if your behind a farm, 
            // set this to the local port used internally.
            int specialPort = 443;

            if (!app.Context.Request.IsSecureConnection 
               || app.Context.Request.Url.Port != specialPort)
            {
               app.Context.Response.Redirect("https://" 
                  + app.Context.Request.ServerVariables["HTTP_HOST"] 
                  + app.Context.Request.RawUrl);    
            }
        }

        public void Dispose()
        {
            // Needed for IHttpModule
        }
    }
}

Then just compile it to a DLL, add it as a reference to your project and place this in web.config:

 <httpModules>
      <add name="HttpsOnlyModule" type="HttpsOnly.HttpsOnlyModule, HttpsOnly" />
 </httpModules>

For @Joe above, "This is giving me a redirect loop. Before I added the code it worked fine. Any suggestions? – Joe Nov 8 '11 at 4:13"

This was happening to me as well and what I believe was happening is that there was a load balancer terminating the SSL request in front of the Web server. So, my Web site was always thinking the request was "http", even if the original browser requested it to be "https".

I admit this is a bit hacky, but what worked for me was to implement a "JustRedirected" property that I could leverage to figure out the person was already redirected once. So, I test for specific conditions that warrant the redirect and, if they are met, I set this property (value stored in session) prior to the redirection. Even if the http/https conditions for redirection are met the second time, I bypass the redirection logic and reset the "JustRedirected" session value to false. You'll need your own conditional test logic, but here's a simple implementation of the property:

    public bool JustRedirected
    {
        get
        {
            if (Session[RosadaConst.JUSTREDIRECTED] == null)
                return false;

            return (bool)Session[RosadaConst.JUSTREDIRECTED];
        }
        set
        {
            Session[RosadaConst.JUSTREDIRECTED] = value;
        }
    }

The IIS7 module will let you redirect.

    <rewrite>
        <rules>
            <rule name="Redirect HTTP 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>
        </rules>
    </rewrite>

If you are using ASP.NET Core you could try out the nuget package SaidOut.AspNetCore.HttpsWithStrictTransportSecurity.

Then you only need to add

app.UseHttpsWithHsts(HttpsMode.AllowedRedirectForGet, configureRoutes: routeAction);

This will also add HTTP StrictTransportSecurity header to all request made using https scheme.

Example code and documentation https://github.com/saidout/saidout-aspnetcore-httpswithstricttransportsecurity#example-code


If SSL support is not configurable in your site (ie. should be able to turn https on/off) - you can use the [RequireHttps] attribute on any controller / controller action you wish to secure.


In IIS10 (Windows 10 and Server 2016), from version 1709 onwards, there is a new, simpler option for enabling HSTS for a website.

Microsoft describe the advantages of the new approach here, and provide many different examples of how to implement the change programmatically or by directly editing the ApplicationHost.config file (which is like web.config but operates at the IIS level, rather than individual site level). ApplicationHost.config can be found in C:\Windows\System32\inetsrv\config.

I've outlined two of the example methods here to avoid link rot.

Method 1 - Edit the ApplicationHost.config file directly Between the <site> tags, add this line:

<hsts enabled="true" max-age="31536000" includeSubDomains="true" redirectHttpToHttps="true" />

Method 2 - Command Line: Execute the following from an elevated command prompt (i.e. right mouse on CMD and run as administrator). Remember to swap Contoso with the name of your site as it appears in IIS Manager.

c:
cd C:\WINDOWS\system32\inetsrv\
appcmd.exe set config -section:system.applicationHost/sites "/[name='Contoso'].hsts.enabled:True" /commit:apphost
appcmd.exe set config -section:system.applicationHost/sites "/[name='Contoso'].hsts.max-age:31536000" /commit:apphost
appcmd.exe set config -section:system.applicationHost/sites "/[name='Contoso'].hsts.includeSubDomains:True" /commit:apphost
appcmd.exe set config -section:system.applicationHost/sites "/[name='Contoso'].hsts.redirectHttpToHttps:True" /commit:apphost

The other methods Microsoft offer in that articles might be better options if you are on a hosted environment where you have limited access.

Keep in mind that IIS10 version 1709 is available on Windows 10 now, but for Windows Server 2016 it is on a different release track, and won't be released as a patch or service pack. See here for details about 1709.


This is a fuller answer based on @Troy Hunt's. Add this function to your WebApplication class in Global.asax.cs:

    protected void Application_BeginRequest(Object sender, EventArgs e)
    {
        // Allow https pages in debugging
        if (Request.IsLocal)
        {
            if (Request.Url.Scheme == "http")
            {
                int localSslPort = 44362; // Your local IIS port for HTTPS

                var path = "https://" + Request.Url.Host + ":" + localSslPort + Request.Url.PathAndQuery;

                Response.Status = "301 Moved Permanently";
                Response.AddHeader("Location", path);
            }
        }
        else
        {
            switch (Request.Url.Scheme)
            {
                case "https":
                    Response.AddHeader("Strict-Transport-Security", "max-age=31536000");
                    break;
                case "http":
                    var path = "https://" + Request.Url.Host + Request.Url.PathAndQuery;
                    Response.Status = "301 Moved Permanently";
                    Response.AddHeader("Location", path);
                    break;
            }
        }
    }

(To enable SSL on your local build enable it in the Properties dock for the project)


It also depends on the brand of your balancer, for the web mux, you would need to look for http header X-WebMux-SSL-termination: true to figure that incoming traffic was ssl. details here: http://www.cainetworks.com/support/redirect2ssl.html


Examples related to c#

How can I convert this one line of ActionScript to C#? Microsoft Advertising SDK doesn't deliverer ads How to use a global array in C#? How to correctly write async method? C# - insert values from file into two arrays Uploading into folder in FTP? Are these methods thread safe? dotnet ef not found in .NET Core 3 HTTP Error 500.30 - ANCM In-Process Start Failure Best way to "push" into C# array

Examples related to asp.net

RegisterStartupScript from code behind not working when Update Panel is used You must add a reference to assembly 'netstandard, Version=2.0.0.0 No authenticationScheme was specified, and there was no DefaultChallengeScheme found with default authentification and custom authorization How to use log4net in Asp.net core 2.0 Visual Studio 2017 error: Unable to start program, An operation is not legal in the current state How to create roles in ASP.NET Core and assign them to users? How to handle Uncaught (in promise) DOMException: The play() request was interrupted by a call to pause() ASP.NET Core Web API Authentication Could not load file or assembly 'CrystalDecisions.ReportAppServer.CommLayer, Version=13.0.2000.0 WebForms UnobtrusiveValidationMode requires a ScriptResourceMapping for jquery

Examples related to vb.net

How to get parameter value for date/time column from empty MaskedTextBox HTTP 415 unsupported media type error when calling Web API 2 endpoint variable is not declared it may be inaccessible due to its protection level Differences Between vbLf, vbCrLf & vbCr Constants Simple working Example of json.net in VB.net How to open up a form from another form in VB.NET? Delete a row in DataGridView Control in VB.NET How to get cell value from DataGridView in VB.Net? Set default format of datetimepicker as dd-MM-yyyy How to configure SMTP settings in web.config

Examples related to webforms

ASP.NET Button to redirect to another page How to use the DropDownList's SelectedIndexChanged event "Specified argument was out of the range of valid values" The ScriptManager must appear before any controls that need it Check if Cookie Exists How to do a Jquery Callback after form submit? "The given path's format is not supported." A potentially dangerous Request.Path value was detected from the client (*) How to create <input type=“text”/> dynamically Create a HTML table where each TR is a FORM

Examples related to https

What's the net::ERR_HTTP2_PROTOCOL_ERROR about? Requests (Caused by SSLError("Can't connect to HTTPS URL because the SSL module is not available.") Error in PyCharm requesting website Android 8: Cleartext HTTP traffic not permitted ssl.SSLError: tlsv1 alert protocol version Invalid self signed SSL cert - "Subject Alternative Name Missing" How do I make a https post in Node Js without any third party module? Page loaded over HTTPS but requested an insecure XMLHttpRequest endpoint How to force Laravel Project to use HTTPS for all routes? Could not create SSL/TLS secure channel, despite setting ServerCertificateValidationCallback Use .htaccess to redirect HTTP to HTTPs