251

I have searched around Google and StackOverflow trying to find a solution to this, but they all seem to relate to ASP.NET etc.

I usually run Linux on my servers but for this one client I am using Windows with IIS 7.5 (and Plesk 10). This being the reason why I am slightly unfamiliar with IIS and web.config files. In an .htaccess file you can use rewrite conditions to detect whether the protocol is HTTPS and redirect accordingly. Is there a simple way to achieve this using a web.config file, or even using the 'URL Rewrite' module that I have installed?

I have no experience with ASP.NET so if this is involved in the solution then please include clear steps of how to implement.

The reason for me doing this with the web.config and not PHP is that I would like to force HTTPS on all assets within the site.

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Ben Carey
  • 16,540
  • 19
  • 87
  • 169
  • 1
    And the inverse: http://stackoverflow.com/questions/25767014/iis-rewrite-rule-in-web-config-to-redirect-https-requests-to-http – Chris Moschini Aug 04 '15 at 20:39
  • Related post - [Best way in asp.net to force https for an entire site?](https://stackoverflow.com/q/47089/465053) – RBT Sep 11 '18 at 14:43

10 Answers10

499

You need URL Rewrite module, preferably v2 (I have no v1 installed, so cannot guarantee that it will work there, but it should).

Here is an example of such web.config -- it will force HTTPS for ALL resources (using 301 Permanent Redirect):

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <clear />
                <rule name="Redirect to https" stopProcessing="true">
                    <match url=".*" />
                    <conditions>
                        <add input="{HTTPS}" pattern="off" ignoreCase="true" />
                    </conditions>
                    <action type="Redirect" url="https://{HTTP_HOST}{REQUEST_URI}" redirectType="Permanent" appendQueryString="false" />
                </rule>
            </rules>
        </rewrite>
    </system.webServer>
</configuration>

P.S. This particular solution has nothing to do with ASP.NET/PHP or any other technology as it's done using URL rewriting module only -- it is processed at one of the initial/lower levels -- before request gets to the point where your code gets executed.

LazyOne
  • 158,824
  • 45
  • 388
  • 391
  • 6
    @BenCarey You should also look at `Strict-Transport-Security` header: http://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security – LazyOne Mar 22 '12 at 13:37
  • 29
    I recommend changing the redirect so that it doesn't append the query string as it is already part of {REQUEST_URI} (otherwise the parameters get added twice). `` – franzo Jan 18 '14 at 05:14
  • @franzo ^^ this should be the correct answer. definitely doubles up the query string with the other example.. – timbrown Jan 28 '14 at 17:19
  • I found this link simple and helpful in making any particular page to accept only https requests - http://support.microsoft.com/kb/239875 – Manik Arora Dec 17 '14 at 10:51
  • Glad that this is ASP.NET agnostic. We've got a pure Angular app that we're running on Windows Azure hosting and this did the track for Angular-specific URLs. I had tried other solutions elsewhere and they broke down for us because they were tied to specific file requests, so on Angular links it didn't work. – Martyn Chamberlin May 02 '15 at 04:13
  • The regex capturing group has no use, so `.*` instead of `(.*)` would work just as fine. Also `` is shorter. – Christiaan Westerbeek Oct 13 '15 at 07:17
  • is it possible to also force to use WWW.domain.com when accessing with a non-www address? so basically 1 redirect to https and WWW URL ? – AlfaTeK Mar 01 '16 at 22:18
  • How do you prevent an SSL error if the SSL cert expires? We just get the FORBIDDEN error from our tests when this happens. – James Wilson Mar 15 '16 at 17:04
  • @JamesWilson Unfortunately it has nothing to do with redirects as it cannot be solved using them -- 1) redirect happens after secure connection is already established (where server/browser should prevent from happening with expired cert) and 2) I do not see any variables that would allow to verify expiration date (if such connection is made ignoring expiration). You have to monitor your expiration dates with some specialised tool (or simple ToDo/Reminder kind of app) I'm afraid. Or create yet another test in your test chain which you run first -- check for certificate expiration. – LazyOne Mar 15 '16 at 17:36
  • @AlfaTeK You could hardcode domain name in place of `{HTTP_HOST}` part. Otherwise you would need to use 2 similar rules (extra check for `www.` presence) -- one for with and one without WWW part. – LazyOne Mar 15 '16 at 17:38
  • Can it be made optional depending on the app setting in web.config? – n0rd May 16 '17 at 23:45
  • @n0rd TBH -- no clue. I'm not a .NET dev so never dealt with app settings stored that way. – LazyOne May 17 '17 at 09:14
  • just to confirm it works with URL Rewrite module 2.1 – fotisgpap Aug 08 '17 at 20:38
  • 10
    This works, but unfortunately also on localhost. To avoid this you can add this to : – wezzix Aug 17 '17 at 15:29
  • 6
    Using AWS Elastic beanstalk, this method was giving me a 302 Too many redirects until I modified: `` to `` – Kevin R. Dec 12 '17 at 21:05
  • I have a Web API project using Bearer token authentication. This doesn't work, if I try to load my web app (angular-js) over HTTP, it doesn't get redirected. But when I try to sign in (posting to /token), I can see that the XHR was permanently moved. But then it results in a 400 Bad Request, because the request following the preflight is targeting the HTTPS version. Any ideas how I can fix this? – Nexus Dec 10 '18 at 01:13
  • I have applied this procedure to my web.config under the but I get this when I browse the site "HTTP Error 500.19 - Internal Server Error". Anybody has any ideas? – Sam Jul 26 '19 at 07:37
  • 1
    @Sam Maybe you do not have URL Rewrite module installed? It does not come with IIS by default, needs to be installed separately. E.g. https://learn.microsoft.com/en-us/iis/extensions/url-rewrite-module/using-the-url-rewrite-module – LazyOne Jul 26 '19 at 07:57
  • Thanks @LazyOne for the helpful tip. I installed the "URL Rewrite" module and it looks like redirecting. Now I get a "403 - Forbidden: Access is denied." Do you know why is this happening? – Sam Jul 29 '19 at 04:31
  • BTW, I did turn off "Require SSL" setting on IIS. – Sam Jul 29 '19 at 05:30
  • I needed this version of URL Rewrite to work on Windows 2016 https://www.microsoft.com/en-us/download/details.aspx?id=47337 – devio Dec 30 '19 at 16:35
  • If you have other rules and this does not work for you try making this the first rule. – Vishnu Babu Apr 09 '21 at 02:12
85

For those using ASP.NET MVC. You can use the RequireHttpsAttribute to force all responses to be HTTPS:

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

Other things you may also want to do to help secure your site:

  1. Force Anti-Forgery tokens to use SSL/TLS:

    AntiForgeryConfig.RequireSsl = true;
    
  2. Require Cookies to require HTTPS by default by changing the Web.config file:

    <system.web>
        <httpCookies httpOnlyCookies="true" requireSSL="true" />
    </system.web>
    
  3. Use the NWebSec.Owin NuGet package and add the following line of code to enable Strict Transport Security (HSTS) across 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: 720).Preload());
    
  4. 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));
    
  5. 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>
    
  6. 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.

Muhammad Rehan Saeed
  • 35,627
  • 39
  • 202
  • 311
  • 4
    The question asks for ASP.NET but does not state WebForms or MVC, so I gave a comprehensive answer for those using MVC (Which does not use the Web.config file to force HTTPS) and yet...downvoted. – Muhammad Rehan Saeed Mar 20 '15 at 10:31
  • I am just speculating, you were probably downvoted because, a) the question was answered 3 years ago, and whilst there may be updates, the solution given still works. And b) because your answer is very complicated for something that is extremely simple. Input is always welcomed on SO, but this answer isn't particularly relevant to the question asked... I may be wrong, it may have been another reason – Ben Carey Mar 20 '15 at 13:05
  • 7
    a) The solution works but things have changed, this prominent, high rated question deserves an updated answer using what's built into MVC. b) The answer tries to cover all bases. The question is not simple, enabling HTTPS over an entire site requires a lot more than changing a web.config file. Readers may be misled into thinking changing a Web.config file is all it takes. Security is hard enough as it is without incomplete/outdated answers. – Muhammad Rehan Saeed Mar 20 '15 at 14:14
  • 12
    In my opinion this is an excellent and valuable answer. When someone googles the topic and is directed to this question I'm glad your answer is here. – President James K. Polk Mar 21 '15 at 11:26
  • @RehanSaeed Care to elaborate when exactly the original web.config solution would fail? – TheSoftwareJedi May 29 '15 at 19:12
  • @TheSoftwareJedi I never said it would fail. The accepted answer is the equivalent of the RequireHttpsAttribute but it uses IIS rather than MVC to do the redirect. The accepted answer does not address the other pieces of the puzzle you need to apply HTTPS to your site. In particular enabling Strict Transport Security and getting the browser to do the 301 redirect and using the preload mode to add further security. – Muhammad Rehan Saeed May 30 '15 at 20:06
  • @rehansaeed it is not the equivalent of an attribute. The question was about redirecting all http requests to https requests "on all assets within the site." An attribute won't do that, and sts and preload aren't relevant. The question isn't about the proper way to setup and use HTTPS. It is merely a question of redirecting ALL requests. – TheSoftwareJedi Jun 01 '15 at 02:58
  • @TheSoftwareJedi The correct way to "force HTTPS on all assets within the site" as asked in MVC is to use RequireHttpsAttribute in conjunction with https or scheme-less URL's (See my answer). The questioner only talks about redirecting when talking about PHP and Apache. The questioner has "no experience with ASP.NET", so we need to guide him to the correct path, not the same path used in PHP/Apache. – Muhammad Rehan Saeed Jun 01 '15 at 09:31
  • 1
    @MuhammadRehanSaeed Nice post. Maybe add SRI to your list? https://scotthelme.co.uk/subresource-integrity/ – Nathan Jan 26 '16 at 12:21
  • @Nathan SRI is great but doesn't have anything to so with HTTPS which is what the question is asking. Good mention though. – Muhammad Rehan Saeed Jan 26 '16 at 13:44
  • 1
    @MuhammadRehanSaeed true - I guess your heading of "Other things you may also want to do to help secure your site" made me think of it :) – Nathan Jan 26 '16 at 13:48
  • @Nathan If you look at the comments of that link, I posted ages ago that I would add that feature to [ASP.NET MVC Boilerplate](https://visualstudiogallery.msdn.microsoft.com/6cf50a48-fc1e-4eaf-9e82-0b2a6705ca7d). Still need to do it, would accept a pull-request. – Muhammad Rehan Saeed Jan 26 '16 at 18:44
  • @Nathan I've implemented SRI for ASP.NET Core using a TagHelper http://rehansaeed.com/subresource-integrity-taghelper-using-asp-net-core/ – Muhammad Rehan Saeed Apr 08 '16 at 07:35
  • I disagree with 5 where you say, "Do not include the scheme of any URL's used in your site or start with https". If a script is served over HTTPS then explicitly request it on HTTPS. It doesn't matter that your site is on HTTP. Most scripts served on both and requested on HTTP just redirect to HTTPS version anyway - so your example of http://ajax.aspnetcdn.com/ajax/bootstrap/3.3.4/bootstrap.min.js redirects to HTTPS - so save a redirect and serve it from canonical origin. – Dan Diplo Sep 19 '16 at 07:58
  • @DanDiplo I've updated 5. Since writing this, I have since learned that the `Content-Security-Policy` HTTP header and Subresource Integrity do not play nice when you imit the scheme in some browsers. It is better to be explicit about HTTPS. – Muhammad Rehan Saeed Sep 19 '16 at 08:33
  • What happens for Web Api? There's no RequireHttpsAttribute in Web Api... – CesarD Oct 05 '16 at 04:08
  • 1
    @CesarD See https://stackoverflow.com/questions/26504409/web-api-2-requirehttps-allowing-http-connection – Muhammad Rehan Saeed Oct 05 '16 at 07:28
  • in my opinion this is a better answer than the web.config, its simpler and it works wh (en debugging locally without setting exceptions to handle localhost, and for cases when you have modified your host to point to your solution in Visual Studio, works a charm – Egli Becerra Jun 18 '20 at 02:11
23

To augment LazyOne's answer, here is an annotated version of the answer.

<rewrite>
  <rules>
     <clear />
     <rule name="Redirect all requests to https" stopProcessing="true">
       <match url="(.*)" />
         <conditions logicalGrouping="MatchAll">
           <add input="{HTTPS}" pattern="off" ignoreCase="true" />
         </conditions>
         <action 
            type="Redirect" url="https://{HTTP_HOST}{REQUEST_URI}" 
            redirectType="Permanent" appendQueryString="false" />
     </rule>
  </rules>
</rewrite>

Clear all the other rules that might already been defined on this server. Create a new rule, that we will name "Redirect all requests to https". After processing this rule, do not process any more rules! Match all incoming URLs. Then check whether all of these other conditions are true: HTTPS is turned OFF. Well, that's only one condition (but make sure it's true). If it is, send a 301 Permanent redirect back to the client at http://www.foobar.com/whatever?else=the#url-contains. Don't add the query string at the end of that, because it would duplicate the query string!

This is what the properties, attributes, and some of the values mean.

  • clear removes all server rules that we might otherwise inherit.
  • rule defines a rule.
    • name an arbitrary (though unique) name for the rule.
    • stopProcessing whether to forward the request immediately to the IIS request pipeline or first to process additional rules.
  • match when to run this rule.
    • url a pattern against which to evaluate the URL
  • conditions additional conditions about when to run this rule; conditions are processed only if there is first a match.
    • logicalGrouping whether all the conditions must be true (MatchAll) or any of the conditions must be true (MatchAny); similar to AND vs OR.
  • add adds a condition that must be met.
    • input the input that a condition is evaluating; input can be server variables.
    • pattern the standard against which to evaluate the input.
    • ignoreCase whether capitalization matters or not.
  • action what to do if the match and its conditions are all true.
    • type can generally be redirect (client-side) or rewrite (server-side).
    • url what to produce as a result of this rule; in this case, concatenate https:// with two server variables.
    • redirectType what HTTP redirect to use; this one is a 301 Permanent.
    • appendQueryString whether to add the query string at the end of the resultant url or not; in this case, we are setting it to false, because the {REQUEST_URI} already includes it.

The server variables are

  • {HTTPS} which is either OFF or ON.
  • {HTTP_HOST} is www.mysite.com, and
  • {REQUEST_URI} includes the rest of the URI, e.g. /home?key=value
    • the browser handles the #fragment (see comment from LazyOne).

See also: https://www.iis.net/learn/extensions/url-rewrite-module/url-rewrite-module-configuration-reference

Shaun Luttin
  • 133,272
  • 81
  • 405
  • 467
  • 1
    One note though: fragment part of URL (from `/home?key=value#fragment`) is not set to the server by browsers as it meant to be used locally. – LazyOne Jan 18 '17 at 11:26
  • @LazyOne Question. We're using the above web.config successfully to redirect from http://www.greenearth.game/about#foo to HTTPS. The switch to HTTPS includes the #foo fragment. Given that the #foo part is not sent to the sever, how does the redirect include it? – Shaun Luttin Jan 18 '17 at 16:39
  • It's handled by browser. Just open network tab in Google Chrome (or similar in Firefox etc) and see what URL is actually requested (e.g. for `http://www.example.com/members#oops` request will be sent to `http://www.example.com/members` which then gets redirected to HTTPS version at `https://www.example.com/members` -- browser does the rest) – LazyOne Jan 18 '17 at 16:56
  • @LazyOne Thank you for that. If I recall correctly, there are a few WebKit bugs that prevent the fragment from being included in redirects. So, this makes sense. https://bugs.webkit.org/show_bug.cgi?id=24175 – Shaun Luttin Jan 18 '17 at 17:00
  • 1
    https://en.wikipedia.org/wiki/Fragment_identifier -- *"Clients are not supposed to send URI-fragments to servers when they retrieve a document, and without help from a local application (see below) fragments do not participate in HTTP redirections"* -- just to be clear in case I misunderstood your last comment. – LazyOne Jan 18 '17 at 17:13
  • @lazyone That's useful information. I think you did misunderstand my last comment. What I meant is that, though the client does not send the fragment to the server, the client is involved in preserving the fragment on the client-side during redirects. My comment was linking that to how some WebKit builds fail in that regard. Am I understanding correctly? – Shaun Luttin Jan 18 '17 at 19:17
10

The accepted answer did not work for me. I followed the steps on this blog.

A key point that was missing for me was that I needed to download and install the URL Rewrite Tool for IIS. I found it here. The result was the following.

<rewrite>
        <rules>
            <remove name="Http to Https" />
            <rule name="Http to Https" enabled="true" patternSyntax="Wildcard" stopProcessing="true">
                <match url="*" />
                <conditions>
                    <add input="{HTTPS}" pattern="off" />
                </conditions>
                <serverVariables />
                <action type="Redirect" url="https://{HTTPS_HOST}{REQUEST_URI}" />
            </rule>
        </rules>
    </rewrite>
Eric
  • 569
  • 1
  • 7
  • 10
1

In .Net Core, follow the instructions at https://learn.microsoft.com/en-us/aspnet/core/security/enforcing-ssl

In your startup.cs add the following:

// Requires using Microsoft.AspNetCore.Mvc;
public void ConfigureServices(IServiceCollection services)
{
    services.Configure<MvcOptions>(options =>
    {
        options.Filters.Add(new RequireHttpsAttribute());
    });`enter code here`

To redirect Http to Https, add the following in the startup.cs

// Requires using Microsoft.AspNetCore.Rewrite;
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

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

    app.UseRewriter(options);
EvilDr
  • 8,943
  • 14
  • 73
  • 133
Oracular Man
  • 1,060
  • 11
  • 15
0

The excellent NWebsec library can upgrade your requests from HTTP to HTTPS using its upgrade-insecure-requests tag within the Web.config:

<nwebsec>
  <httpHeaderSecurityModule>
    <securityHttpHeaders>
      <content-Security-Policy enabled="true">
        <upgrade-insecure-requests enabled="true"  />
      </content-Security-Policy>
    </securityHttpHeaders>
  </httpHeaderSecurityModule>
</nwebsec>
WhatIsHeDoing
  • 541
  • 1
  • 6
  • 20
0

I was not allowed to install URL Rewrite in my environment, so, I found another path.

Adding this to my web.config added the error rewrite and worked on IIS 7.5:

<system.webServer>
    <httpErrors errorMode="Custom" defaultResponseMode="File" defaultPath="C:\WebSites\yoursite\" >    
    <remove statusCode="403" subStatusCode="4" />
    <error statusCode="403" subStatusCode="4" responseMode="File" path="redirectToHttps.html" />
</httpErrors>

Then, following the advice here: https://www.sslshopper.com/iis7-redirect-http-to-https.html

I configured the IIS website to require SSL and created the html file that does the redirect (redirectToHttps.html) upon the 403 (Forbidden) error:

<html>
<head><title>Redirecting...</title></head>
<script language="JavaScript">
function redirectHttpToHttps()
{
    var httpURL= window.location.hostname + window.location.pathname + window.location.search;
    var httpsURL= "https://" + httpURL;
    window.location = httpsURL;
}
redirectHttpToHttps();
</script>
<body>
</body>
</html>

I hope someone finds this useful as I could not find all of the pieces in one place anywhere else.

George Birbilis
  • 2,782
  • 2
  • 33
  • 35
Spencer Sullivan
  • 527
  • 6
  • 13
0

I am using below code and it perfect works for me, hope it will help you.

<configuration>
<system.webServer>
    <rewrite>
        <rules>
            <rule name="Force redirect to https" stopProcessing="true">
                <match url="(.*)" />
                <conditions>
                    <add input="{HTTPS}" pattern="^OFF$" />
                </conditions>
                <action type="Redirect" url="https://{HTTP_HOST}{REQUEST_URI}" appendQueryString="false" />
            </rule>
        </rules>
    </rewrite>
</system.webServer>
SadikAli
  • 594
  • 1
  • 4
  • 21
0

You have to configure redirection as below, when you are using IIS 10 with application load balancer on AWS.

        <rule name="Force HTTPS" enabled="true" stopProcessing="true">
            <match url="(.*)" />
            <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
                <add input="{HTTP_X_Forwarded_Proto}" pattern="^https$" negate="true" />
                <add input="{HTTP_HOST}" pattern="^(www\.)?dname\.com$" />
            </conditions>
            <action type="Redirect" url="https://www.dname.com{REQUEST_URI}" />
        </rule>

If you have sub domain redirection use below:

        <!--START REDIRECT TO HTTPS-->
        <rule name="Force HTTPS" enabled="true" stopProcessing="true">
            <match url="(.*)" />
            <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
                <add input="{HTTP_X_Forwarded_Proto}" pattern="^https$" negate="true" />
                <add input="{HTTP_HOST}" pattern="subdomain\.domain\.com$" />
            </conditions>
            <action type="Redirect" url="https://subdomain.domain.com{REQUEST_URI}" />
        </rule> 
        <!--END REDIRECT TO HTTPS-->    
Aditya Y
  • 651
  • 6
  • 12
-8

A simple way is to tell IIS to send your custom error file for HTTP requests. The file can then contain a meta redirect, a JavaScript redirect and instructions with link, etc... Importantly, you can still check "Require SSL" for the site (or folder) and this will work.

</configuration>
</system.webServer>
    <httpErrors>
        <clear/>
        <!--redirect if connected without SSL-->
        <error statusCode="403" subStatusCode="4" path="errors\403.4_requiressl.html" responseMode="File"/>
    </httpErrors>
</system.webServer>
</configuration>