Blog Post

IIS Support Blog
11 MIN READ

Addressing TLS 1.3 Compatibility Issues in IIS Express on Windows 11

MattHamrick's avatar
MattHamrick
Icon for Microsoft rankMicrosoft
Aug 29, 2025

If you are using a Windows 11 workstation and encountering errors using IIS Express with an ASP.NET project (or any project being tested on IIS Express) that utilizes client certificates (also known as "mutual TLS" or "mTLS"), this post is for you.

As Windows 10 approaches its end-of-support, these issues are becoming more prevalent as this problem has always existed on Win11. This guide will help you identify the symptoms, understand the causes, and explore potential solutions to resolve these client certificate problems in IIS Express on Win11.

Note: many of the concepts will apply to both full IIS and IIS Express, on Windows 11 and Windows Server 2022 and 2025; but this post's focus is on IIS Express on workstations.

Symptom

When launching a web app in IIS Express, the error you see if you're affected by this problem depends on which build of Win11 you're on.

Windows 11 24H2 or newer (and Windows Server 2025):

IIS detailed error page showing HTTP 500.0 Internal Server Error with the error code 0x80070032 originating from the IIS Web Core module

The error code 0x80070032 translates to ERROR_NOT_SUPPORTED.

Windows 11 Before 24H2 (and Windows Server 2022):

Standard Microsoft Edge error page showing error: "ERR_CONNECTION_RESET"

The main error there is "ERR_CONNECTION_RESET" indicating the browser's connection was unexpectedly terminated via receiving a TCP RST.

Cause

Most likely at some point in the past, the applicationhost.config file used by IIS Express was manually modified to include the below lines in some form:

<location path="" overrideMode="Allow">
<system.webServer>
  <security>
    <access sslFlags="SslNegotiateCert" />
  </security>

One common place for this file is here:

[solution directory]\.vs\[project name]\config\applicationhost.config

The critical line is #4 with the "SslNegotiateCert" option. This could also have "SslRequireCert" to experience the same issue. By default, those sslFlags are not set in the file. Those options ("SslNegotiateCert" or "SslRequireCert") tell IIS Express, at the beginning of processing a request, to check for a client certificate. Unfortunately, at this time, that will not work on Windows 11 without some kind of workaround.

The underlying cause here is the use of TLS 1.3 by default for inbound and outbound traffic on Win11, and TLS 1.3 does not support a TLS concept known as "renegotiation." This is the same problem detailed by JasonXu in this post for Windows Server 2022 with full IIS. With this post being about IIS Express, which works differently and is managed differently than full IIS, the solutions will be mostly different here. If you want more background and technical details on how this works, continue reading past the "Solution" section below. There I will also explain the differences in symptoms/errors on the different Win11 builds.

Solutions

At the time of this writing, in late August 2025, there is no quick fix in IIS Express or the VS solution/project. I am honestly not sure if there will be a fix and what it will look like if there is. For now, there are a few ways to work around this:

  1. Disable TLS 1.3 for inbound/server sessions on the workstation. I recommend this over other workarounds since it requires no code or any changes to the app or solution/project, and only affects inbound TLS traffic (which is typically minimal on workstations). Any outbound traffic from your browsers or any other clients won't be affected. The downside is this is machine-wide. Again, this shouldn't be a problem since on a workstation there shouldn't be anything depending on inbound TLS 1.3 specifically; however, your scenario may be different.
  2. Update the http.sys binding via netsh to negotiate a client certificate in in the initial TLS handshake. A potential problem with this, in my opinion, is since these bindings are setup during the Visual Studio installation process (I believe), updates or changes to VS might also update these bindings and reset them to a default state, undoing your changes. I honestly don't know if this would happen and I did not test it, so it may not be a problem at all. You should be able to modify just the one binding as well. Doing this requires an administrative command prompt.
  3. Undo/Remove the client cert configuration in the aforementioned applicationhost.config and modify app code to negate the need for client certs in this particular environment. This could get complicated and messy, but it's certainly an option.

If anything changes or other solutions come up, I will update this post if possible.

Solution #1 - Disable inbound TLS 1.3

This is done via the registry. Using your method of choice (Group Policy Preferences, .reg file, PowerShell, manually, etc.), make the below changes:

HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.3\Server
REG_DWORD "DisabledByDefault" = 1
REG_DWORD "Enabled" = 0

Note: depending on prior configurations, the entire path above may not exist. Just create any missing folders/keys. 

Here are some PowerShell commands to do it (need admin):

New-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols" -Name "TLS 1.3" -ErrorAction SilentlyContinue

New-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.3" -Name "Server" -ErrorAction SilentlyContinue

Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.3\Server" -Name "DisabledByDefault" -Value 1 -Type DWord

Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.3\Server" -Name "Enabled" -Value 0 -Type DWord

 

A reboot is needed for Schannel to pick the changes up. 

Solution #2 - Enable Client Certificate Negotiation via Netsh

For this option you would need to identify any https:// URLs in affected projects that need to be changed. One place to see it is in Visual Studio's project properties pane:

screenshot showing a Visual Studio 2022 project properties pane with the "SSL URL" property highlighted, containing a value of "https://localhost:44339/"

Once you have the URLs to be changed, open an admin command prompt (either on its own or via the Command Prompt option if using the Terminal app) and run the below command to see the current configuration for that binding, substituting the appropriate port for the one used in your project. The below command is to view the binding shown above:

C:\>netsh http show ssl ipport=0.0.0.0:44339

SSL Certificate bindings:
-------------------------

    IP:port                      : 0.0.0.0:44339
    Certificate Hash             : 92e10294489ad41c2a773403b8bc4cd166b03dc6
    Application ID               : {214124cd-d05b-4309-9af9-9caa44b2b74a}
    Certificate Store Name       : MY
    Verify Client Certificate Revocation : Enabled
    Verify Revocation Using Cached Client Certificate Only : Disabled
    Use Revocation Freshness Time : Disabled
    Verify Revocation Using Cached URLs Only : Disabled
    Disable Authority Info Access : Disabled
    Usage Check                  : Enabled
    Revocation Freshness Time    : 0
    URL Retrieval Timeout        : 0
    Ctl Identifier               : (null)
    Ctl Store Name               : (null)
    DS Mapper Usage              : Disabled
  Negotiate Client Certificate : Disabled
    Reject Connections           : Disabled
    Disable HTTP2                : Not Set
    Disable QUIC                 : Not Set
    Disable TLS1.2               : Not Set
    Disable TLS1.3               : Not Set
  Disable OCSP Stapling        : Not Set

The "Negotiate Client Certificate" option is the one we need to change. Changing involves deleting the binding and recreating it with the property set. You will need to make note of the Certificate Hash, Application ID, and Certificate Store Name properties to be able to recreate the binding. 

Delete the binding:

C:\>netsh http delete ssl ipport=0.0.0.0:44339

SSL Certificate successfully deleted

Recreate the binding with the updated setting (this is where you need to re-enter the certificate hash, application ID, and cert store name in their respective parameters, exactly as they were before):

C:\>netsh http add ssl ipport=0.0.0.0:44339 certhash=92e10294489ad41c2a773403b8bc4cd166b03dc6 appid={214124cd-d05b-4309-9af9-9caa44b2b74a} certstorename=MY clientcertnegotiation=Enable

SSL Certificate successfully added

 

No reboot is needed after making this change. The same change will need to be made for any binding used where IIS Express is checking for a client certificate.

Solution #3 - App Changes

I don't cover code changes here, but if you want to remove the client cert check from IIS Express, then you can delete this part of its applicationhost.config:

<access sslFlags="SslNegotiateCert" />

This "sslFlags" property can hold multiple, comma-separate values. Thus, if other values like "Ssl" are present, you can just delete the "SslNegotiateCert" and the "SslRequireCert" values if either or both are present. If both are present, both must be deleted.

At that point, you can modify the app, if needed, to not depend on client certificates for local testing. You would want to ensure to do this in a way that won't affect production traffic (i.e. if in production this app receives requests sent to https://localhost, then you shouldn't use localhost as a way to determine if it's IIS Express or not). You can do something like using an environment variable, checking the process name (IIS Express uses "iisexpress.exe", etc. Just be sure to not add this work into a hot code path so in production the code is not executed more than needed. Ideally, this would be a check during application startup so it's only done once. 

Background/Extra Information

Like full IIS, IIS Express does not directly open network sockets and listen for requests from a client at the network level. Both IIS and IIS Express use the Windows HTTP Server APIs for the actual inbound HTTP legwork. We in Microsoft support typically refer to these APIs and functionality collectively as "http.sys" since that kernel driver is where this is all handled. In other words, when a client sends a request to either IIS or IIS Express, the actual server doing the listening for and acceptance of inbound connections, any TLS stuff, and the initial HTTP request parsing, is handled by http.sys in the kernel. Once all that work is done, a handle to the HTTP request is delivered to IIS/IIS Express (in user mode) for processing. "Processing" here includes the instantiation of IIS-specific data structures, calling various modules to do things, invoking the hosted application to do its own work, etc. Once the processing is complete, IIS/IIS Express calls http.sys back to send the response.

The important part is that IIS/IIS Express doesn't have much control over the parameters of the network connection or the TLS session if applicable, and IIS/IIS Express only get involved once the actual HTTP request has been received, parsed, validated, and delivered from http.sys. Thus, when IIS/IIS Express is configured to check for and/or require a client certificate, it can only make this check long after the TLS handshake was completed. Unless the client was asked on the initial TLS handshake for a certificate, they would not have sent one.

TLS 1.2 and prior versions allow a process called renegotiation to take place. This means a second handshake will take place inside the existing encrypted tunnel, and the server can then ask the client for a certificate in that second handshake. Http.sys handles all this work with Schannel in the kernel, and IIS/IIS Express is called back for further processing once it's all done. All that extra work is invisible to the hosted application and is generally seamless.

TLS 1.3 does not allow renegotiation to take place. It does have the ability to request client authentication after the handshake (called "post-handshake client authentication", RFC), but it's 1.3-specific and the majority of clients, browsers included, have not implemented support for this extension at the time of this writing (it's optional per the RFC). Thus, unless a client certificate was negotiated from the very beginning, during the TLS handshake, one cannot be asked for or sent later with most clients. That puts IIS and IIS Express in a tight spot because they reside so late in the process - they have to tell http.sys up-front, before they get involved in the specific request, that they want a client certificate since they can't get one after-the-fact. 

This was fixed in full IIS in Windows Server 2025 by adding a "Negotiate Client Certificate" checkbox to the https-type of site binding. This works because an IIS https site binding is really just a higher-level representation of an http.sys SSL binding (the ones listed in netsh http show ssl that was used earlier in this post in solution #2). I like to think of bindings as specific doors into a web site. Different bindings are different doors that require different parameters for entry. So when you create a site binding, IIS under-the-hood is calling http.sys APIs to create one of its bindings as well. That new "Negotiate Client Certificate" option maps to the "clientcertnegotiation" property which configures http.sys to request a client certificate on the initial TLS handshake, thus allowing full IIS to work with TLS 1.3+client certificates. Before this fix, and currently on Windows Server 2022, options to fix were/are similar to what's in this post for IIS Express. You could manually update the http.sys binding (like solution #2). You could also select the option to "Disable TLS 1.3 over TCP" which would effectively force TLS 1.2. Or even disable TLS 1.3 inbound. So, contrary to what I wrote higher up, there is some level of control that full IIS has over the http.sys bindings, and thus the TLS session, but not much of it is exposed or configurable to server administrators.

However, with IIS Express there is even less control; in fact, there's none. The crucial difference is in how and when those http.sys bindings are created. I mentioned above that with full IIS, they are created dynamically by IIS calling into http.sys when site bindings are configured. A binding in http.sys is tied to a specific site binding in IIS. The bindings used by IIS Express are created long in advance, and not by IIS Express itself. IIS Express has no control over those bindings. When IIS Express is not running, those bindings still exist. Those http.sys bindings are not tied to anything running in IIS Express (there is an active relationship when it's actually running to ensure requests get delivered to the IIS Express process, but the binding will remain after the process exits). You could also reconfigure the project to use a different port in Visual Studio, which would change which pre-created http.sys binding IIS Express gets bound to at runtime (if doing this you would also have to apply one of the solutions to the new binding that is being used). I do think fixing this problem will be much more complicated on the IIS Express and Visual Studio product sides since the bindings aren't created at runtime.

 

Lastly, why are symptoms/errors different depending on the build of Win11 being run? This behavior difference is due to how http.sys reacts to the client certificate request from IIS Express (or full IIS if that's being used). The symptom difference will also be the difference between Windows Server 2022 and 2025 and up, since those are codebases that map to different Win11 builds. 

On Win11 before 24H2 (and Windows Server 2022), http.sys terminates the client's connection when a client cert was asked for, when the client does not support post-handshake client authentication. Thus, when IIS/IIS Express ask for a certificate when one was not negotiated initially, the connection is terminated. I cannot say why that is the behavior that was implemented in http.sys, but that's how it is. When this happens, IIS/IIS Express is informed of the termination so it can tear its objects and such down, but it can't do anything about it.

On Win11 24H2 and above (and Windows Server 2025), http.sys now returns a "not supported" error to IIS instead of terminating the connection. This allows IIS to continue executing and send its HTTP 500 detailed error page (it can't send an error page to the user if http.sys terminates the underlying connection) with the 0x80070032 error code.

Unfortunately, neither of the above symptoms is very helpful for diagnosing the actual cause of the problem, which is one of the reasons I wrote this post. I hope it has helped!

 

Thanks for reading!

Updated Aug 29, 2025
Version 1.0
No CommentsBe the first to comment