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.

Task
Install on a server
Platforms
  • Windows
  • IIS
Who it is for
Level
Practitioner · 5 steps
On this page
  1. Before you start
  2. Steps
  3. 1. Check the PFX before you import it
  4. 2. Import it into the computer’s Personal store
  5. 3. Make sure the intermediate is in place
  6. 4. Add the HTTPS binding
  7. 5. Plan the renewal now
  8. Check it worked
  9. If something goes wrong
  10. Why this works
  11. Go deeper

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:

Try it: Chain checkerOpen the full tool

Runs in your browser. Nothing you paste is sent anywhere.

Every certificate the server sends, leaf first. The output of openssl s_client -showcerts -connect example.com:443 works as it is: we ignore the text around the certificates.

If something goes wrong

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

Early edition. Checked against the standards; an independent expert review is in progress. Found a mistake? Tell us.

Last reviewed Edition 0.9