Why can't I log in to a Windows-protected IIS 7.5 directory on the server?

Solution 1:

You are almost certainly running into the Windows loopback check that was introduced with IIS 5.1. This is a security feature to avoid certain types of reflection attacks against the system.

Microsoft has a KB article describing workarounds. They basically boil down to modifying the registry to either disable the loopback check, or to allow certain hostnames (e.g. your local host name or site name) to back-connect.

You can quickly disable the check via PowerShell:

New-ItemProperty HKLM:\System\CurrentControlSet\Control\Lsa -Name "DisableLoopbackCheck" -Value "1" -PropertyType dword

Below are Microsoft's official instructions. Note that although the below instructions indicate a reboot, I've found that IE usually picks up the change right away.

Method 1: Specify host names (Preferred method if NTLM authentication is desired)

  1. Set the DisableStrictNameChecking registry entry to 1.
  2. Click Start, click Run, type regedit, and then click OK.
  3. In Registry Editor, locate and then click the following registry key: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0
  4. Right-click MSV1_0, point to New, and then click Multi-String Value.
  5. Type BackConnectionHostNames, and then press ENTER.
  6. Right-click BackConnectionHostNames, and then click Modify.
  7. In the Value data box, type the host name or the host names for the sites that are on the local computer, and then click OK.
  8. Quit Registry Editor, and then restart the IISAdmin service.

Method 2: Disable the loopback check (less-recommended method)

  1. Set the DisableStrictNameChecking registry entry to 1.
  2. Click Start, click Run, type regedit, and then click OK.
  3. In Registry Editor, locate and then click the following registry key: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa
  4. Right-click Lsa, point to New, and then click DWORD Value.
  5. Type DisableLoopbackCheck, and then press ENTER.
  6. Right-click DisableLoopbackCheck, and then click Modify.
  7. In the Value data box, type 1, and then click OK.
  8. Quit Registry Editor, and then restart your computer.

Addendum:

To set the DisableStrictNameChecking registry entry to 1:

  1. Click Start, click Run, type regedit, and then click OK.
  2. In Registry Editor, locate and then click the following registry key: HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\LanmanServer\Parameters
  3. Right-click Parameters, point to New, and then click DWORD Value.
  4. Type DisableStrictNameChecking, and then press ENTER.
  5. Right-click DisableStrictNameChecking, and then click Modify.
  6. In the Value data box, type 1, and then click OK.
  7. Quit Registry Editor, and then restart your computer.

Solution 2:

Here are the PowerShell commandlets that I wrote to manage the loopback check settings. It includes code that attempts to get the hostnames for all IIS web sites that use Windows Authentication and sets the back connection host names.

Import-Module WebAdministration

function Add-BackConnectionHostName
{
    <#
    .SYNOPSIS
    Adds the back connection hostnames that will bypass the server loopback check.
    .DESCRIPTION
    Adds the hostname to the list of back connection hostnames that will bypass the server loopback check. Back connection host names  
    can be used to address the problem with IIS sites using Windows Authentication that is described in Microsoft KB896861.
    .EXAMPLE
    Add-BackConnectionHostName mywebsite.mydomain.tld
    .EXAMPLE
    Add-BackConnectionHostName mywebsite1.mydomain.tld, mywebsite2.mydomain.tld
    .PARAMETER Hostname
    The Hostname to add to the back connection hostnames list.
    .LINK
    Remove-BackConnectionHostName
    Get-BackConnectionHostName
    Enable-ServerLoopbackCheck
    Disable-ServerLoopbackCheck
    Get-ServerLoopbackCheck
    "You receive error 401.1 when you browse a Web site that uses Integrated Authentication and is hosted on IIS 5.1 or a later version" (http://support.microsoft.com/en-us/kb/896861)
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param
    (
        [Parameter(ValueFromPipeline = $true, Mandatory = $true)]
        [string] $Hostname
    )

    begin
    {
        $keyPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0"
        $propertyName = "BackConnectionHostNames"
        $key = Get-Item $keyPath
        $property = $null
        $propertyValues = $null

        if ($key -ne $null)
        {
            $property = Get-ItemProperty $keyPath -Name $propertyName -ErrorAction SilentlyContinue

            if ($property -eq $null)
            {
                $property = New-ItemProperty $keyPath -Name $propertyName -Value $null -PropertyType ([Microsoft.Win32.RegistryValueKind]::MultiString) -ErrorAction Stop

                Write-Verbose "Created the $($propertyName) property."
            }

            if ($property -ne $null)
            {
                $propertyValues = $property.$propertyName
            }
        }
    }

    process
    {
        if ($property -ne $null)
        {
            foreach ($hostNameValue in $Hostname)
            {
                if ([string]::IsNullOrWhiteSpace($hostName) -eq $false -and $propertyValues -notcontains $hostNameValue)
                {
                    $propertyValues += $hostNameValue

                    Write-Verbose "Added $($hostName) to the back connection hostnames."
                }
                else
                {
                    Write-Verbose "Back connection host names already has an entry for $($hostName)."
                }
            }
        }
    }

    end
    {
        if ($propertyValues -ne $null)
        {
            $propertyValues = $propertyValues | ?{ [string]::IsNullOrWhiteSpace($_) -eq $false } | Sort -Unique
            Set-ItemProperty $keyPath -Name $propertyName -Value $propertyValues
        }
    }
}

function Remove-BackConnectionHostName
{
    <#
    .SYNOPSIS
    Removes the hostname from the list of back connection hostnames that will bypass the server loopback check.
    .DESCRIPTION
    Removes the hostname from the list of back connection hostnames that will bypass the server loopback check.
    .EXAMPLE
    Remove-BackConnectionHostName mywebsite.mydomain.tld
    .EXAMPLE
    Remove-BackConnectionHostName mywebsite1.mydomain.tld, mywebsite2.mydomain.tld
    .PARAMETER Hostname
    The Hostname to remove from the back connection hostnames list.
    .LINK
    Add-BackConnectionHostName
    Get-BackConnectionHostName
    Enable-ServerLoopbackCheck
    Disable-ServerLoopbackCheck
    Get-ServerLoopbackCheck
    "You receive error 401.1 when you browse a Web site that uses Integrated Authentication and is hosted on IIS 5.1 or a later version" (http://support.microsoft.com/en-us/kb/896861)
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param
    (
        [Parameter(ValueFromPipeline = $true, Mandatory = $true)]
        [string] $Hostname
    )

    begin
    {
        $keyPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0"
        $propertyName = "BackConnectionHostNames"
        $key = Get-Item $keyPath
        $property = $null
        $propertyValues = $null

        if ($key -ne $null)
        {
            $property = Get-ItemProperty $keyPath -Name $propertyName -ErrorAction SilentlyContinue

            if ($property -ne $null)
            {
                $propertyValues = $property.$propertyName
            }
            else
            {
                Write-Verbose "The $($propertyName) property was not found."
            }
        }
    }

    process
    {
        if ($property -ne $null)
        {
            foreach ($hostNameValue in $Hostname)
            {
                if ($propertyValues -contains $hostNameValue)
                {
                    $propertyValues = $propertyValues | ? { $_ -ne $hostName }

                    Write-Verbose "Removed $($hostName) from the $($propertyName) property."
                }
                else
                {
                    Write-Verbose "No entry for $($hostName) was found in the $($propertyName) property."
                }
            }
        }
    }

    end
    {
        if ($property -ne $null)
        {
            $propertyValues = $propertyValues | ?{ [string]::IsNullOrWhiteSpace($_) -eq $false } | Sort -Unique

            if ($propertyValues.Length -ne 0)
            {
                Set-ItemProperty $keyPath -Name $propertyName -Value $propertyValues
            }
            else
            {
                Remove-ItemProperty $keyPath -Name $propertyName

                Write-Verbose "No entries remain after removing $($hostName). The $($propertyName) property was removed."
            }
        }
    }
}

function Get-BackConnectionHostName
{
    <#
    .SYNOPSIS
    Gets the list of back connection hostnames that will bypass the server loopback check.
    .DESCRIPTION
    Gets the back connection hostnames that will bypass the server loopback check. Back connection host names can be used to address 
    the problem with IIS sites using Windows Authentication that is described in Microsoft KB896861.
    .EXAMPLE
    Get-BackConnectionHostName
    .LINK
    Add-BackConnectionHostName
    Remove-BackConnectionHostName
    Enable-ServerLoopbackCheck
    Disable-ServerLoopbackCheck
    Get-ServerLoopbackCheck
    "You receive error 401.1 when you browse a Web site that uses Integrated Authentication and is hosted on IIS 5.1 or a later version" (http://support.microsoft.com/en-us/kb/896861)
    #>
    [CmdletBinding(SupportsShouldProcess = $false)]
    param
    (
    )

    begin
    {
        $keyPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0"
        $propertyName = "BackConnectionHostNames"
        $key = Get-Item $keyPath
        $property = $null

        if ($key -ne $null)
        {
            $property = Get-ItemProperty $keyPath -Name $propertyName -ErrorAction SilentlyContinue

            if ($property -eq $null)
            {
                Write-Verbose "The $($propertyName) property was not found."
            }
        }
    }

    process
    {
        $propertyValues = $null

        if ($property -ne $null)
        {
            $propertyValues = $property.$propertyName
        }

        return $propertyValues
    }

    end
    {
    }
}

function Enable-ServerLoopbackCheck
{
    <#
    .SYNOPSIS
    Enables the server loopback check. Enabled is the normal state for a Windows Server.
    .DESCRIPTION
    Enables the server loopback check. Having the loopback check enabled is the normal state for a Windows Server. Disabling the loopback check can be used to address 
    the problem with IIS sites using Windows Authentication that is described in Microsoft KB896861. It is NOT the preferred method. See the KB article for more details.
    .EXAMPLE
    Enable-ServerLoopbackCheck
    .LINK
    Add-BackConnectionHostName
    Remove-BackConnectionHostName
    Get-BackConnectionHostName
    Enable-ServerLoopbackCheck
    Get-ServerLoopbackCheck
    "You receive error 401.1 when you browse a Web site that uses Integrated Authentication and is hosted on IIS 5.1 or a later version" (http://support.microsoft.com/en-us/kb/896861)
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param
    (
    )

    begin
    {
        $keyPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa"
        $propertyName = "DisableLoopbackCheck"
        $key = Get-Item $keyPath
        $property = $null

        if ($key -ne $null)
        {
            $property = Get-ItemProperty $keyPath -Name $propertyName -ErrorAction SilentlyContinue

            if ($property -eq $null)
            {
                Write-Verbose "The $($propertyName) property was not found."
            }
        }
    }

    process
    {
        if ($property -ne $null)
        {
            Set-ItemProperty $keyPath -Name $propertyName -Value 0
        }
    }

    end
    {
    }
}

function Disable-ServerLoopbackCheck
{
    <#
    .SYNOPSIS
    Disables the server loopback check for all hostnames. Enabled is the normal state for a Windows Server.
    .DESCRIPTION
    Disables the server loopback check for all hostnames. Having the loopback check enabled is the normal state for a Windows Server. Disabling the loopback check can be used 
    to address the problem with IIS sites using Windows Authentication that is described in Microsoft KB896861. It is NOT the preferred method. See the KB article for more details.
    .EXAMPLE
    Disable-ServerLoopbackCheck
    .LINK
    Add-BackConnectionHostName
    Remove-BackConnectionHostName
    Get-BackConnectionHostName
    Enable-ServerLoopbackCheck
    Get-ServerLoopbackCheck
    "You receive error 401.1 when you browse a Web site that uses Integrated Authentication and is hosted on IIS 5.1 or a later version" (http://support.microsoft.com/en-us/kb/896861)
    #>
    [CmdletBinding(SupportsShouldProcess = $true)]
    param
    (
    )

    begin
    {
        $keyPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa"
        $propertyName = "DisableLoopbackCheck"
        $key = Get-Item $keyPath
        $property = $null

        if ($key -ne $null)
        {
            $property = Get-ItemProperty $keyPath -Name $propertyName -ErrorAction SilentlyContinue

            if ($property -eq $null)
            {
                Write-Verbose "The $($propertyName) property was not found."
            }
        }
    }

    process
    {
        if ($property -ne $null)
        {
            Set-ItemProperty $keyPath -Name $propertyName -Value 1
        }
        else
        {
            $property = New-ItemProperty $keyPath -Name $propertyName -PropertyType ([Microsoft.Win32.RegistryValueKind]::DWord) -Value 1
        }
    }

    end
    {
    }
}

function Get-ServerLoopbackCheck
{
    <#
    .SYNOPSIS
    Gets the status of the server loopback check. Enabled is the normal state for a Windows Server.
    .DESCRIPTION
    Gets the status of the server loopback check. Having the loopback check enabled is the normal state for a Windows Server. Disabling the loopback check can be used 
    to address the problem with IIS sites using Windows Authentication that is described in Microsoft KB896861. It is NOT the preferred method. See the KB article for 
    more details.
    .EXAMPLE
    Get-ServerLoopbackCheck
    .LINK
    Add-BackConnectionHostName
    Remove-BackConnectionHostName
    Get-BackConnectionHostName
    Enable-ServerLoopbackCheck
    Disable-ServerLoopbackCheck
    "You receive error 401.1 when you browse a Web site that uses Integrated Authentication and is hosted on IIS 5.1 or a later version" (http://support.microsoft.com/en-us/kb/896861)
    #>
    [CmdletBinding(SupportsShouldProcess = $false)]
    param
    (
    )

    begin
    {
        $keyPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa"
        $propertyName = "DisableLoopbackCheck"
        $key = Get-Item $keyPath
        $property = $null

        if ($key -ne $null)
        {
            $property = Get-ItemProperty $keyPath -Name $propertyName -ErrorAction SilentlyContinue
        }
    }

    process
    {
        $loopbackCheckStatus = "Enabled"

        if ($property -ne $null)
        {
            switch ($property)
            {
                0 { $loopbackCheckStatus = "Enabled" }
                1 { $loopbackCheckStatus = "Disabled" }
                default { $loopbackCheckStatus = "Unknown" }
            }
        }

        return $loopbackCheckStatus
    }

    end
    {
    }
}

function Get-WebsiteHostname
{
    <#
    .SYNOPSIS
    Gets the hostnames for the IP addresses bound to a web site.
    .DESCRIPTION
    Gets the hostnames for the IP addresses bound to a web site. Where a host header exists, the host header is used; otherwise, the IP address is looked up
    in DNS to see if a PTR record exists.
    .EXAMPLE
    Get-WebSiteHostname $webSite
    .EXAMPLE
    Get-WebSiteHostname -Name 'Default Web Site'
    .EXAMPLE
    Get-Website | Get-WebSiteHostname
    .LINK
    Get-Website 
    #>
    [CmdletBinding(SupportsShouldProcess = $false)]
    param
    (
        [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Mandatory = $true)]
        [string] $Name
    )

    process
    {
        $siteHostnames = @()

        foreach ($webSiteName in $Name)
        {
            $bindings = Get-WebBinding -Name $Name

            foreach ($binding in $bindings)
            {
                $bindingInfo = $binding.bindingInformation.Split(':')
                $hostHeader = $bindingInfo[2]
                $bindingInfoAddress = $null
                $isValidIP = [System.Net.IPAddress]::TryParse($bindingInfo[0], [ref] $bindingInfoAddress)
                $siteHostname = $null

                if ($bindingInfo -eq '*')
                {
                    Write-Warning "The $($webSiteName) web site has a binding address set to All Unassigned."
                }
                elseif ([string]::IsNullOrWhiteSpace($hostHeader) -eq $false)
                {
                    $siteHostname = $hostHeader
                    Write-Verbose "The $($webSiteName) web site has a host header set to $($siteHostname)."
                }
                elseif ($isValidIP -eq $true)
                {
                    $siteHostname = (Resolve-DnsName $bindingInfoAddress -DnsOnly PTR -ErrorAction SilentlyContinue).NameHost

                    if ($siteHostname -ne $null)
                    {
                        Write-Verbose "The $($webSiteName) web site has an IP Address $($bindingInfoAddress) that resolves to $($siteHostname)."
                    }
                    else
                    {
                        Write-Warning "The $($webSiteName) web site has an IP Address $($bindingInfoAddress) with no PTR record."
                    }
                }
            }

            if ($siteHostname -ne $null)
            {
                $siteHostnames += $siteHostname
            }
        }

        return $siteHostnames | Sort -Unique
    }
}

# Use the IIS administration commandlets and the ones above to do the 
# following:
#   1. Get all the IIS web sites that use Windows authentication.
#   2. Get the hostnames from either the host header setting or the
#      DNS reverse lookup of the hostnames from the IP address.
#   3. Add the hostnames to the BackConnectionHostNames registry key.
#   4. Display the contents of the BackConnectionHostNames registry key.

$windowsAuthenticatedWebSites = Get-Website | ?{ (Get-WebConfiguration -Filter '/system.web/authentication' -PSPath $_.PSPath).mode -eq 'Windows' }
$webSiteHostnames = $windowsAuthenticatedWebSites | Get-WebsiteHostname
$webSiteHostNames | Add-BackConnectionHostName

Get-BackConnectionHostName