How-to guide · Install on a server
How to install a certificate in IIS
In one sentence
IIS serves your certificate with its private key and intermediate on an HTTPS binding with SNI, and renewed certificates get rebound instead of silently expiring.
- Platforms
- Windows
- IIS
- Who it is for
- Level
- Practitioner · 5 steps
On this page
Before you start
- Windows Server with IIS 10 (IIS 8.5 or later for automatic rebind), and an administrator account.
- Your certificate with its private key: a PFX (PKCS #12) file and its password, or the CA’s response to a CSR you created on this server (create a CSR on Windows).
- The CA’s intermediate certificate, if the PFX does not contain it.
- For Let’s Encrypt, win-acme does all of this and keeps it renewed.
Why it matters: IIS refers to the certificate by thumbprint in the computer’s store, and Windows sends the chain it can build from that computer’s stores. A missing intermediate or private key is the usual cause of a broken HTTPS site on IIS.
Steps
1. Check the PFX before you import it
certutil -dump reads the file without importing anything. For our test PFX, built with OpenSSL from a throwaway test CA, Windows 11 showed:
certutil -dump -p <password> www.pfx
================ Certificate 0 ================
Issuer: CN=Example Corp Root CA, O=Example Corp, C=BE
Subject: CN=Example Corp Issuing CA 1, O=Example Corp, C=BE
No key provider information
...
================ Certificate 1 ================
Issuer: CN=Example Corp Issuing CA 1, O=Example Corp, C=BE
Subject: CN=intranet.example.com
Provider = Microsoft Software Key Storage Provider
Encryption test passed
CertUtil: -dump command completed successfully.
You want two things: the server certificate with a private key (Encryption test passed), and the intermediate. The intermediate has no key, which is correct. If you have PEM files instead, build the PFX with OpenSSL and include the intermediate with -certfile:
openssl pkcs12 -export -out www.pfx -inkey www.key -in www.crt -certfile intermediate.crt -name "www.example.com"
Older Windows versions may not open the AES-protected PFX files OpenSSL 3 makes by default; see PFX and PEM.
2. Import it into the computer’s Personal store
In PowerShell as administrator, with Import-PfxCertificate:
$pfxPassword = Read-Host -AsSecureString -Prompt "PFX password"
Import-PfxCertificate -FilePath C:\certs\www.pfx -CertStoreLocation Cert:\LocalMachine\My -Password $pfxPassword
Without -Exportable the key cannot be exported again, which is what you want on a web server; keep the original PFX in your secrets vault instead. In IIS Manager, the same is server node > Server Certificates > Import.
If you created the CSR in IIS Manager, use Complete Certificate Request with the CA’s .cer file instead. The key is already on the server.
3. Make sure the intermediate is in place
Import-PfxCertificate puts every certificate in the PFX into the store you name: “Certificates with and without private keys in the PFX file are imported”. So after step 2 the intermediate sits in the Personal store next to your certificate. Check the computer’s Intermediate Certification Authorities store:
Get-ChildItem Cert:\LocalMachine\CA | Where-Object Subject -like '*Issuing CA*' | Format-List Subject, Thumbprint, NotAfter
If it is missing, import it there; never into Trusted Root:
Import-Certificate -FilePath C:\certs\intermediate.cer -CertStoreLocation Cert:\LocalMachine\CA
4. Add the HTTPS binding
In IIS Manager: select the site, Bindings > Add. Choose type https, IP address All Unassigned, port 443, type the Host name, tick Require Server Name Indication, and pick the certificate.
Or in PowerShell with the IISAdministration module (New-IISSiteBinding):
$cert = Get-ChildItem Cert:\LocalMachine\My |
Where-Object { $_.HasPrivateKey -and $_.DnsNameList.Unicode -contains 'www.example.com' } |
Sort-Object NotAfter -Descending | Select-Object -First 1
New-IISSiteBinding -Name "Default Web Site" -BindingInformation "*:443:www.example.com" `
-CertificateThumbPrint $cert.Thumbprint -CertStoreLocation "Cert:\LocalMachine\My" `
-Protocol https -SslFlag Sni
With SNI, several sites can share one IP address and port, each with its own certificate. Protocol versions and cipher suites are mostly set for the whole server in Windows (Schannel); the binding’s SSL flags, such as DisableLegacyTLS, adjust a few of them per site. See How to configure TLS securely.
5. Plan the renewal now
A renewed certificate has a new thumbprint, and the binding still points to the old one. Choose one of these:
- Certificate Rebind (IIS 8.5 and later): Server Certificates > Enable Automatic Rebind of Renewed Certificate. It reacts to Windows’ certificate-renewal event, so it works for renewals through Windows (manual renewal or autoenrollment), not for a new certificate you import (Microsoft).
- An ACME client such as win-acme, which imports the new certificate and updates the binding itself.
- A script that repeats steps 2 to 4, run by your deployment tooling.
Check it worked
On the server, confirm the binding and its certificate:
Get-IISSiteBinding -Name "Default Web Site"
netsh http show sslcert hostnameport=www.example.com:443
netsh shows the Certificate Hash (the thumbprint) bound to that name and port. Then test from another machine, as a client sees it:
openssl s_client -connect www.example.com:443 -servername www.example.com </dev/null 2>/dev/null | grep -E '^ [0-9] s:|^ i:|Verify return code'
You want two certificates, the server certificate first, and Verify return code: 0 (ok). Paste the -showcerts output into the chain checker:
If something goes wrong
- The certificate does not appear in the IIS list, or the binding fails. It has no usable private key:
Get-ChildItem Cert:\LocalMachine\My | Format-Table Subject, HasPrivateKey. Import the PFX that contains the key. If you completed a CSR made on this server,certutil -repairstore my <serial number>can re-link the certificate to its key. - Browsers work, but curl, Java or apps report
unable to verify the first certificateorunable to get local issuer certificate. The intermediate is not being sent: repeat step 3. Windows clients may fetch it themselves, which hides the problem. See build the full chain. - A different site’s certificate is served. The binding lacks SNI or a host name, or the client sends no SNI and gets the default
*:443binding. NET::ERR_CERT_COMMON_NAME_INVALID. The certificate does not cover the host name. Check it with the hostname matcher.NET::ERR_CERT_DATE_INVALIDafter a renewal. The binding still points to the old certificate (step 5).- .NET clients say
The remote certificate is invalid. One of the causes above; the error page lists how to tell them apart.
Why this works
IIS uses Windows’ TLS stack, Schannel. When a client connects, Windows looks up the binding for the IP, port and SNI host name, loads the certificate and its private key from the computer store, and builds the certificate chain to send from the local stores. The key signs the TLS handshake; the client then checks the chain against its own roots. See Certificate chains and path validation, The TLS 1.3 handshake and, for renewals, Inventory, expiry and renewal.
Go deeper
- Import-PfxCertificate: importing a PFX into the computer store.
- New-IISSiteBinding: HTTPS bindings, SNI and the other SSL flags.
- Certificate Rebind in IIS 8.5: how automatic rebinding works and when it triggers.
- IIS 8.0 Server Name Indication: how SNI bindings work in IIS.
Early edition. Checked against the standards; an independent expert review is in progress. Found a mistake? Tell us.