HSTS Settings for a Web Site <hsts>
Overview
The <hsts>
element of the <site>
element contains attributes that allow you to configure HTTP Strict Transport Security (HSTS) settings for a site on IIS 10.0 version 1709 and later.
Note
If the <hsts>
element is configured in both the <siteDefaults>
section and in the <site>
section for a specific site, the configuration in the <site>
section is used for that site.
Compatibility
Version | Notes |
---|---|
IIS 10.0 version 1709 | The <hsts> element of the <site> element was introduced in IIS 10.0 version 1709. |
IIS 10.0 | N/A |
IIS 8.5 | N/A |
IIS 8.0 | N/A |
IIS 7.5 | N/A |
IIS 7.0 | N/A |
IIS 6.0 | N/A |
Setup
The <hsts>
element of the <site>
element is included in the default installation of IIS 10.0 version 1709 and later.
How To
There is no user interface that lets you configure the <hsts>
element of the <site>
element for IIS 10.0 version 1709. For examples of how to configure the <hsts>
element of the <site>
element programmatically, see the Sample Code section of this document.
Configuration
Attributes
Attribute | Description |
---|---|
enabled |
Optional Boolean attribute. Specifies whether HSTS is enabled (true) or disabled (false) for a site. If HSTS is enabled, the Strict-Transport-Security HTTP response header is added when IIS replies an HTTPS request to the web site. The default value is false . |
max-age |
Optional uint attribute. Specifies the max-age directive in the Strict-Transport-Security HTTP response header field value. The default value is 0 . |
includeSubDomains |
Optional Boolean attribute. Specifies whether the includeSubDomains directive is included in the Strict-Transport-Security HTTP response header field value. Note: Enable this attribute only if all subdomains indeed offer HTTP-based service over TLS/SSL. The default value is false . |
preload |
Optional Boolean attribute. Specifies whether the preload directive is included in the Strict-Transport-Security HTTP response header field value. Note: Enable this attribute only if the domain of the site has been submitted for inclusion in the HSTS preload list. The default value is false . |
redirectHttpToHttps |
Optional Boolean attribute. Specifies whether HTTP to HTTPS redirection is enabled (true) or disabled (false) for a site. Note: Enabling redirectHttpToHttps enforces the site-level HTTP to HTTPS redirection. When IIS redirects an HTTP request, it replaces the URI scheme with "https" and ignores the port component. Make sure that the redirection destination provides HTTP-based service over TLS/SSL on standard port 443. The default value is false . |
Child Elements
None.
Configuration Sample
The following configuration sample shows a web site named Contoso that has HSTS enabled with both HTTP and HTTPS bindings. The max-age attribute is set as 31536000 seconds (a year) so that the user agents will regard the host as a Known HSTS Host within a year after the reception of the Strict-Transport-Security header field. The includeSubDomains attribute is set as true to specify that the HSTS policy applies to this HSTS Host (contoso.com) as well as any subdomain (for example, www.contoso.com
or marketing.contoso.com
). Finally, the redirectHttpToHttps attribute is set as true so that all HTTP requests to the site will be redirected to HTTPS.
<site name="Contoso" id="1">
<application path="/" applicationPool="Contoso">
<virtualDirectory path="/" physicalPath="C:\Contoso\Content" />
</application>
<bindings>
<binding protocol="http" bindingInformation="*:80:contoso.com" />
<binding protocol="https" bindingInformation="*:443:contoso.com" sslFlags="0" />
</bindings>
<hsts enabled="true" max-age="31536000" includeSubDomains="true" redirectHttpToHttps="true" />
</site>
Sample Code
The following code samples enable HSTS for a web site named Contoso with both HTTP and HTTPS bindings. The sample sets max-age attribute as 31536000 seconds (a year), and enables both the includeSubDomains and the redirectHttpToHttps attributes.
AppCmd.exe
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
Note
You must be sure to set the commit parameter to apphost
when you use AppCmd.exe to configure these settings. This commits the configuration settings to the appropriate location section in the applicationHost.config file.
C#
using System;
using System.Text;
using Microsoft.Web.Administration;
internal static class Sample
{
private static void Main()
{
using(ServerManager serverManager = new ServerManager())
{
Configuration config = serverManager.GetApplicationHostConfiguration();
ConfigurationSection sitesSection = config.GetSection("system.applicationHost/sites");
ConfigurationElementCollection sitesCollection = sitesSection.GetCollection();
ConfigurationElement siteElement = FindElement(sitesCollection, "site", "name", @"Contoso");
if (siteElement == null) throw new InvalidOperationException("Element not found!");
ConfigurationElement hstsElement = siteElement.GetChildElement("hsts");
hstsElement["enabled"] = true;
hstsElement["max-age"] = 31536000;
hstsElement["includeSubDomains"] = true;
hstsElement["redirectHttpToHttps"] = true;
serverManager.CommitChanges();
}
}
private static ConfigurationElement FindElement(ConfigurationElementCollection collection, string elementTagName, params string[] keyValues)
{
foreach (ConfigurationElement element in collection)
{
if (String.Equals(element.ElementTagName, elementTagName, StringComparison.OrdinalIgnoreCase))
{
bool matches = true;
for (int i = 0; i < keyValues.Length; i += 2)
{
object o = element.GetAttributeValue(keyValues[i]);
string value = null;
if (o != null)
{
value = o.ToString();
}
if (!String.Equals(value, keyValues[i + 1], StringComparison.OrdinalIgnoreCase))
{
matches = false;
break;
}
}
if (matches)
{
return element;
}
}
}
return null;
}
}
VB.NET
Imports System
Imports System.Text
Imports Microsoft.Web.Administration
Module Sample
Sub Main()
Dim serverManager As ServerManager = New ServerManager
Dim config As Configuration = serverManager.GetApplicationHostConfiguration
Dim sitesSection As ConfigurationSection = config.GetSection("system.applicationHost/sites")
Dim sitesCollection As ConfigurationElementCollection = sitesSection.GetCollection
Dim siteElement As ConfigurationElement = FindElement(sitesCollection, "site", "name", "Contoso")
If (siteElement Is Nothing) Then
Throw New InvalidOperationException("Element not found!")
End If
Dim hstsElement As ConfigurationElement = siteElement.GetChildElement("hsts")
hstsElement("enabled") = True
hstsElement("max-age") = 31536000
hstsElement("includeSubDomains") = True
hstsElement("redirectHttpToHttps") = True
serverManager.CommitChanges()
End Sub
Private Function FindElement(ByVal collection As ConfigurationElementCollection, ByVal elementTagName As String, ByVal ParamArray keyValues() As String) As ConfigurationElement
For Each element As ConfigurationElement In collection
If String.Equals(element.ElementTagName, elementTagName, StringComparison.OrdinalIgnoreCase) Then
Dim matches As Boolean = True
Dim i As Integer
For i = 0 To keyValues.Length - 1 Step 2
Dim o As Object = element.GetAttributeValue(keyValues(i))
Dim value As String = Nothing
If (Not (o) Is Nothing) Then
value = o.ToString
End If
If Not String.Equals(value, keyValues((i + 1)), StringComparison.OrdinalIgnoreCase) Then
matches = False
Exit For
End If
Next
If matches Then
Return element
End If
End If
Next
Return Nothing
End Function
End Module
JavaScript
var adminManager = new ActiveXObject('Microsoft.ApplicationHost.WritableAdminManager');
adminManager.CommitPath = "MACHINE/WEBROOT/APPHOST";
var sitesSection = adminManager.GetAdminSection("system.applicationHost/sites", "MACHINE/WEBROOT/APPHOST");
var sitesCollection = sitesSection.Collection;
var siteElementPos = FindElement(sitesCollection, "site", ["name", "Contoso"]);
if (siteElementPos == -1) throw "Element not found!";
var siteElement = sitesCollection.Item(siteElementPos);
var hstsElement = siteElement.ChildElements.Item("hsts");
hstsElement.Properties.Item("enabled").Value = true;
hstsElement.Properties.Item("max-age").Value = 31536000;
hstsElement.Properties.Item("includeSubDomains").Value = true;
hstsElement.Properties.Item("redirectHttpToHttps").Value = true;
adminManager.CommitChanges();
function FindElement(collection, elementTagName, valuesToMatch)
{
for (var i = 0; i < collection.Count; i++)
{
var element = collection.Item(i);
if (element.Name == elementTagName)
{
var matches = true;
for (var iVal = 0; iVal < valuesToMatch.length; iVal += 2)
{
var property = element.GetPropertyByName(valuesToMatch[iVal]);
var value = property.Value;
if (value != null)
{
value = value.toString();
}
if (value != valuesToMatch[iVal + 1])
{
matches = false;
break;
}
}
if (matches)
{
return i;
}
}
}
return -1;
}
VBScript
Set adminManager = WScript.CreateObject("Microsoft.ApplicationHost.WritableAdminManager")
adminManager.CommitPath = "MACHINE/WEBROOT/APPHOST"
Set sitesSection = adminManager.GetAdminSection("system.applicationHost/sites", "MACHINE/WEBROOT/APPHOST")
Set sitesCollection = sitesSection.Collection
siteElementPos = FindElement(sitesCollection, "site", Array("name", "Contoso"))
If siteElementPos = -1 Then
WScript.Echo "Element not found!"
WScript.Quit
End If
Set siteElement = sitesCollection.Item(siteElementPos)
Set hstsElement = siteElement.ChildElements.Item("hsts")
hstsElement.Properties.Item("enabled").Value = True
hstsElement.Properties.Item("max-age").Value = 31536000
hstsElement.Properties.Item("includeSubDomains").Value = True
hstsElement.Properties.Item("redirectHttpToHttps").Value = True
adminManager.CommitChanges()
Function FindElement(collection, elementTagName, valuesToMatch)
For i = 0 To CInt(collection.Count) - 1
Set element = collection.Item(i)
If element.Name = elementTagName Then
matches = True
For iVal = 0 To UBound(valuesToMatch) Step 2
Set property = element.GetPropertyByName(valuesToMatch(iVal))
value = property.Value
If Not IsNull(value) Then
value = CStr(value)
End If
If Not value = CStr(valuesToMatch(iVal + 1)) Then
matches = False
Exit For
End If
Next
If matches Then
Exit For
End If
End If
Next
If matches Then
FindElement = i
Else
FindElement = -1
End If
End Function
IISAdministration PowerShell Cmdlets
Import-Module IISAdministration
Reset-IISServerManager -Confirm:$false
Start-IISCommitDelay
$sitesCollection = Get-IISConfigSection -SectionPath "system.applicationHost/sites" | Get-IISConfigCollection
$siteElement = Get-IISConfigCollectionElement -ConfigCollection $sitesCollection -ConfigAttribute @{"name"="Contoso"}
$hstsElement = Get-IISConfigElement -ConfigElement $siteElement -ChildElementName "hsts"
Set-IISConfigAttributeValue -ConfigElement $hstsElement -AttributeName "enabled" -AttributeValue $true
Set-IISConfigAttributeValue -ConfigElement $hstsElement -AttributeName "max-age" -AttributeValue 31536000
Set-IISConfigAttributeValue -ConfigElement $hstsElement -AttributeName "includeSubDomains" -AttributeValue $true
Set-IISConfigAttributeValue -ConfigElement $hstsElement -AttributeName "redirectHttpToHttps" -AttributeValue $true
Stop-IISCommitDelay
Remove-Module IISAdministration