Are you seeing 401’s too often for HTTP web requests?

We often hear concerns from our customers saying that they are seeing performance issues because of extra round trips being made to the Web server while requesting for web pages. Where they expected just one sequence of 401.2/401.1/200 (NTLM) or 401.2/200 (Kerberos) they are seeing the sequence more often for the subsequent page requests. They would like a way to avoid making extra round trips from the client to the server for an already authenticated page request.

Why such a behaviour?? Read on…

Before we proceed let’s clear the two terminologies.

Connection-based authentication: Client that is authenticated after an initial HTTP request stays authenticated for the duration of the HTTP Keep-Alive session. This means that the server will assume subsequent requests coming on the same connection without an authorization header, to be executed as the last authenticated user. Subsequent requests on the same connection will not be challenged to send the authorization headers as part of their HTTP requests.

Clients only have to re-authenticate if they make another HTTP request by using a different client TCP port. This scenario occurs when a new HTTP Keep-Alive session must be established.

Request-based authentication: This means that the client has to be re-authenticated for each HTTP request. This behaviour causes an increase in the network traffic because of the extra round trips made to the server from the client.

So with that clear in mind here are the scenarios you may encounter for the IIS versions when using Windows Integrated authentication for your web application.

Authentication method: NTLM
IIS 6.0

NTLM on IIS 6.0 uses Connection-based authentication. This behaviour is governed by a metabase property called AuthPersistSingleRequest. By default this value is set to false which means when using NTLM authentication you should see lesser round trips for every page requests. The default value of FALSE is applicable to all versions of IIS, i.e. IIS 5.0, 6.0 and 7.0/7.5.

Here is how IIS log will look like when it is set to the default value of FALSE.

 1st request
 GET /iisstart.htm - 401 2 2148074254  
GET /iisstart.htm - 401 1 0  
GET /iisstart.htm DOMAIN1\User1 200 0 0  
GET /pagerror.gif DOMAIN1\User1 200 0 0  
GET /magnifyingglass.ico - 401 2 2148074254  
GET /magnifyingglass.ico - 401 1 0  
GET /magnifyingglass.ico DOMAIN1\User1 200 0 0  

2nd request (refresh)  

GET /iisstart.htm DOMAIN1\User1 304 0 0  
GET /magnifyingglass.ico DOMAIN1\User1 304 0 0  
GET /pagerror.gif DOMAIN1\User1 304 0 0  

You may see some requests showing additional 401.2/401.1/200(or 304) here because they are initiating the request on a different client socket and hence a new connection is being established. 

If we have the AuthPersistSingleRequest value set to TRUE then each request will have to be authenticated irrespective of the previous request on the same connection. Consequently requests will require authorization headers to be sent. Here is the step you need to follow to enable the setting in such a case.

 > cscript.exe adsutil.vbs set w3svc/<Website ID>/AuthPersistSingleRequest TRUE  

Here is how the IIS log looks like after setting AuthPersistSingleRequest to TRUE.

 1st request  

GET /iisstart.htm - 401 2 2148074254  
GET /iisstart.htm - 401 1 0  
GET /iisstart.htm DOMAIN1\User1 200 0 0  
GET /pagerror.gif - 401 2 2148074254  
GET /pagerror.gif - 401 1 0  
GET /magnifyingglass.ico - 401 2 2148074254  
GET /magnifyingglass.ico - 401 1 0  
GET /pagerror.gif DOMAIN1\User1 200 0 0  
GET /magnifyingglass.ico DOMAIN1\User1 200 0 0  

2nd request (refresh)  

GET /iisstart.htm - 401 2 2148074254  
GET /iisstart.htm - 401 1 0  
GET /iisstart.htm DOMAIN1\User1 304 0 0  
GET /pagerror.gif - 401 2 2148074254  
GET /pagerror.gif - 401 1 0  
GET /magnifyingglass.ico - 401 2 2148074254  
GET /magnifyingglass.ico - 401 1 0  
GET /pagerror.gif DOMAIN1\User1 304 0 0  
GET /magnifyingglass.ico DOMAIN1\User1 304 0 0  

Check this link and this link for other possible settings when proxies are involved.

IIS 7.0

AuthPersistSingleRequest in IIS 7 is set as an attribute under windowsAuthentication in applicationhost.config file.

 <windowsAuthentication
   enabled="True|False"
   authPersistSingleRequest="True|False" 
   UseKernelMode
>
   <providers>...</providers>
</windowsAuthentication>  

https://msdn.microsoft.com/en-us/library/aa347472.aspx

This completes our discussion on NTLM.

Authentication method: KERBEROS

IIS 6.0

Kerberos used to be Connection-based in IIS 5.0. Starting IIS 6.0+ Kerberos is by default Request-based authentication instead of Connection-based as in NTLM. You may see performance hit because of this behaviour because of the extra round trips.

So, if we have Kerberos instead of NTLM being used for your web application then we need to ensure that the following hotfix is applied (This hotfix requires the prerequisite as Win2k3 Sp1. If you are already running on SP2 you don’t need to install this hotfix.). Ensure the Registry key EnableKerbAuthPersist is set as per the KB article 917557.

Here is how the IIS log will look like when this hotfix/key is not set.

 GET /iisstart.htm - 401 2 2148074254 
GET /iisstart.htm DOMAIN1\User1 200 0 0 
GET /pagerror.gif - 401 2 2148074254 
GET /magnifyingglass.ico - 401 2 2148074254 
GET /pagerror.gif DOMAIN1\User1 200 0 0 
GET /magnifyingglass.ico DOMAIN1\User1 200 0 0

After adding the registry key EnableKerbAuthPersist and restarting IIS services we should see something like this:

 GET /iisstart.htm - 401 2 2148074254 
GET /iisstart.htm DOMAIN1\User1 200 0 0 
GET /magnifyingglass.ico - 401 2 2148074254 
GET /pagerror.gif DOMAIN1\User1 200 0 0 
GET /magnifyingglass.ico DOMAIN1\User1 200 0 0 
GET /iisstart.htm DOMAIN1\User1 304 0 0 
GET /pagerror.gif DOMAIN1\User1 304 0 0 
GET /magnifyingglass.ico DOMAIN1\User1 304 0 0 
GET /iisstart.htm DOMAIN1\User1 304 0 0 
GET /magnifyingglass.ico DOMAIN1\User1 304 0 0 
GET /pagerror.gif DOMAIN1\User1 304 0 0

IIS 7.0

EnableKerbAuthPersist is no more applicable in IIS 7. Instead use the attribute AuthPersistNonNTLM in the applicationhost.config file.

 <windowsAuthentication
   enabled="True|False"
   AuthPersistNonNTLM="True | False" 
   UseKernelMode
>
   <providers>...</providers>
</windowsAuthentication>

https://msdn.microsoft.com/en-us/library/aa347472.aspx

The above article seems to have incorrect wording. Ensure we set the attribute to true to have connection-based authentication.

 <location path="Default Web Site"> 
        <system.webServer> 
            <security> 
                <authentication> 
                    <anonymousAuthentication enabled="false" /> 
                    <windowsAuthentication enabled="true" authPersistNonNTLM="true"  /> 
                </authentication> 
            </security> 
        </system.webServer> 
</location>

Caveat: If we are going via a Proxy server IIS will only persist requests per request and that is by by design.

Going forward if we have Win2k8 R2+ as the web server using Windows Integrated Authentication (user mode) and Win7+IE8 as the client, Nego2 schema based authentication can be feasible.

Improvements in Win7+IE8 will further reduce the unnecessary traffic for Kerberos based requests.

Cheers to life!!