Client certificate authentication (mutual TLS) is the strongest access control IIS offers without a VPN, but it fails in confusing ways: a 403.16, a 403.7, or a browser that silently ignores your prompt. This guide covers configuring IIS to require client certificates, mapping certificates to Windows identities, and the certificate-trust chain problems that cause most failures.
How IIS client certificate auth actually works
During the TLS handshake, the server requests a certificate from the client. IIS then applies one of four SSL settings: Ignore, Accept, Require, or Require with a mapping. “Require” validates the certificate against trusted roots; “Require with mapping” additionally ties the certificate to a Windows account or uses a custom rule.
| SSL client cert mode | Behaviour | Use case |
|---|---|---|
| Ignore | Client certs are ignored entirely | Public sites |
| Accept | Requests one, validates if present | Optional/soft rollout |
| Require | Rejects handshake or returns 403 if absent/invalid | Locked-down internal apps |
| Require + mapping | Maps cert to a Windows user (Many-to-One or One-to-One) | Per-user authorization without AD password |
Step 1: require client certificates at the site level
# Requires WebAdministration module, elevated shell
Import-Module WebAdministration
# Require a client certificate on the default site
Set-WebConfigurationProperty -PSPath 'IIS:\Sites\Default Web Site' `
-Filter 'system.webServer/security/access' -Name 'sslFlags' `
-Value 'Ssl, SslRequireCert'
# Or accept but do not require (useful during rollout)
Set-WebConfigurationProperty -PSPath 'IIS:\Sites\Default Web Site' `
-Filter 'system.webServer/security/access' -Name 'sslFlags' `
-Value 'Ssl, SslNegotiateCert'
# Verify
Get-WebConfigurationProperty -PSPath 'IIS:\Sites\Default Web Site' `
-Filter 'system.webServer/security/access' -Name 'sslFlags'
- Ssl — require SSL for this site.
- SslNegotiateCert — ask the client for a certificate (Accept).
- SslRequireCert — demand a valid client certificate (Require).
- SslMapCert — use the configured mapping (One-to-One or Many-to-One) to log the user on as a Windows identity.
Step 2: make the issuing CA trusted by the server
The number one reason “Require” fails is that the server does not trust the CA that signed the client certificate. Client certs from an internal PKI must have their root and any intermediate CAs imported into the machine’s Trusted Root and Intermediate stores.
# Import a root CA into the machine store (replace path)
Import-Certificate -FilePath "C:\PKI\root-ca.cer" `
-CertStoreLocation "Cert:\LocalMachine\Root"
Import-Certificate -FilePath "C:\PKI\intermediate-ca.cer" `
-CertStoreLocation "Cert:\LocalMachine\CA"
# List trusted roots and confirm yours is present
Get-ChildItem Cert:\LocalMachine\Root | Where-Object Subject -match 'Contoso'
Step 3: map a certificate to a Windows identity
Mapping lets IIS log the request in as a real Windows user, so NTFS ACLs and User.Identity.Name work as usual. A Many-to-One mapping applies one rule (for example, match the Subject’s CN) to many certificates; One-to-One maps an exact cert to a specific account.
# One-to-one mapping: exact certificate thumbprint to a Windows account
$site = "IIS:\Sites\Default Web Site"
$hash = (Get-PfxCertificate "C:\certs\client.pfx").Thumbprint
New-WebConfigurationProperty -PSPath $site `
-Filter "system.webServer/security/authentication/iisClientCertificateMappingAuthentication" `
-Name "." -Value @{enabled='true';oneToOneCertificateMappingsEnabled='true'}
New-Item -Path "$site/iisClientCertificateMappingAuthentication/oneToOneMappings" `
-Value @{certificate=[System.Convert]::ToBase64String(
(Get-PfxCertificate "C:\certs\client.cer").RawData);
userName='CONTOSO\svc_webclient'; password='P@ssw0rd!'} -Force
If you prefer mapping by Subject rather than by exact certificate — useful when certificates are renewed regularly — use Many-to-One with a rule like MatchAll and an expression extracting the CN. Enable the IIS Client Certificate Mapping Authentication role feature first, or these cmdlets fail with “configuration section not recognized”.
Diagnosing the classic errors
| Error | Meaning | Fix |
|---|---|---|
| 403.7 | Client certificate required but not presented | Browser has no cert installed, or you chose the wrong one |
| 403.16 | Client certificate untrusted or invalid | Import the signing CA chain into the machine store |
| 403.13 | Client certificate revoked | Check CRL/OCSP reachability from the server |
| 403.17 | Certificate expired | Renew the client cert |
| No prompt at all | TLS renegotiation disabled or wrong cipher | Confirm the site is HTTPS-only and uses TLS 1.2+ |
Two subtleties cause most support tickets. First, certificate selection is client-side — Chrome and Edge use the Windows store, Firefox historically used its own, so the same user can fail in one browser and succeed in another. Second, revoked-certificate checking requires the server to reach the CRL distribution point, which a firewalled VPS often cannot. If the CDP is unreachable and iisClientCertificateMappingAuthentication is strict, valid certs get rejected — publish the CRL somewhere reachable or relax revocation checking deliberately.
Hardening and operational notes
Client certificates move the authentication burden to the TLS layer, which is a strong control, but the operational work is in certificate lifecycle rather than configuration. Plan for renewal, revocation, and distribution before you enforce “Require”, otherwise a single expired certificate locks a user out with a 403.17 and no helpful log line.
- Keep “Accept” first. Roll out with SslNegotiateCert, collect telemetry on which certificates are presented, then switch to Require. Flipping straight to Require on a live site locks out every client without a cert.
- Short validity, automated renewal. Prefer 90-day client certificates renewed automatically by the PKI or an ACME client over multi-year certs that nobody tracks.
- Publish the CRL somewhere reachable. A VPS that cannot reach the CA’s CRL distribution point will treat valid certificates as unverifiable.
- Log the subject. Enable IIS logging of the client certificate subject so failed authentications are traceable to a specific certificate, not just a source IP.
Testing without a browser in the way
# Test mTLS from the command line (curl 8+ on the server or a workstation)
curl -v --cert client.pem --key client.key https://internal.example.com/ -o NUL
# Or with a PFX
curl --cert client.pfx:password https://internal.example.com/ -I
When curl reports a handshake failure, run it with -v and read the certificate request and verify lines — they tell you instantly whether the server asked for a cert and whether the chain validated. If client certificates are only one part of a broader lockdown, pair them with the rest of the baseline is covered in our server hardening guide, and size the instance so revocation and OCSP lookups do not compete with your app for bandwidth — see our Windows VPS comparison.



