This site is an archive and is no longer updated.
    Back to Guides
    Web Servers

    IIS SSL Certificate Configuration

    Complete guide to SSL/TLS certificate installation, binding configuration, and security hardening on Microsoft Internet Information Services.

    18-22 min readFor IIS 8–10 on Windows Server 2012 R2–2022
    LinkedInX
    IIS SSL Certificate Configuration Guide

    Version coverage: This guide covers IIS 8.0 through 10.0 on Windows Server 2012 R2 through 2022. Both GUI-based (IIS Manager) and PowerShell/command-line approaches are included for all operations.

    What this guide covers:

    • CSR generation - IIS Manager GUI and PowerShell/certreq methods
    • Certificate installation - Complete request, PFX import, intermediate chains
    • HTTPS bindings - Including SNI for multiple certificates on one IP
    • TLS hardening - Disable legacy protocols, configure strong cipher suites
    • Enterprise features - Centralized Certificate Store for web farms

    1. IIS SSL Architecture Overview

    Understanding how IIS handles SSL/TLS is essential for proper configuration and troubleshooting.

    Key Components

    ComponentPurpose
    HTTP.sysKernel-mode driver that handles SSL termination (not IIS worker process)
    Personal StoreTraditional certificate storage location (Cert:\LocalMachine\My)
    Web Hosting StoreOptimized for IIS, scales better for SNI scenarios (IIS 8+)
    ApplicationHost.configIIS configuration file containing site and binding definitions

    Traffic Flow

    Client Browser → HTTP.sys (SSL termination) → IIS Worker Process → Application

    SSL termination happens in HTTP.sys at the kernel level, not in the IIS worker process. This is why certificate changes sometimes require running net stop http /y to take effect.

    Caution: Running net stop http /y stops ALL sites on the server, not just the one you're working on. Schedule maintenance windows for production servers.

    Why This Matters:

    • • HTTP.sys architecture explains why some certificate changes require net stop http /y
    • • Web Hosting store scales better for multi-tenant and SNI scenarios
    • • Private key accessibility issues often trace to store location or permissions

    SSL Bindings Storage:

    SSL bindings are stored in HTTP.sys, not just IIS configuration. Use netsh http show sslcert to view the actual bindings that HTTP.sys is using.

    2. Generating a Certificate Signing Request (CSR)

    A CSR contains your public key and identity information. Generate it on the server where the certificate will be installed.

    Method 1: IIS Manager GUI

    1. Open IIS Manager → Select Server node → Server Certificates
    2. Actions pane → Create Certificate Request
    3. Fill in Distinguished Name fields:
      • Common Name: FQDN (e.g., www.example.com)
      • Organization, Organizational Unit, City, State, Country
    4. Cryptographic settings:
      • Provider: Microsoft RSA SChannel (or ECDSA for ECC)
      • Bit length: 2048 minimum for RSA; use 3072 for 3+ year validity or high-security workloads (4096 adds minimal security but higher CPU overhead)
    5. Save CSR to file location

    Method 2: PowerShell/Certreq (Recommended for Automation)

    Create INF file for CSR generation:

    # Create INF file for CSR generation
    $inf = @"
    [Version]
    Signature="$Windows NT$"
    
    [NewRequest]
    Subject = "CN=www.example.com, O=Example Corp, L=Atlanta, S=Georgia, C=US"
    KeySpec = 1
    KeyLength = 2048          ; Use 3072 or 4096 for longer validity or higher security
    Exportable = TRUE
    MachineKeySet = TRUE
    SMIME = FALSE
    PrivateKeyArchive = FALSE
    UserProtected = FALSE
    UseExistingKeySet = FALSE
    ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
    ; Note: CNG provider (Microsoft Software Key Storage Provider) offers
    ; better HSM support but may have compatibility issues with legacy software
    ProviderType = 12
    RequestType = PKCS10
    KeyUsage = 0xa0
    HashAlgorithm = SHA256
    
    [EnhancedKeyUsageExtension]
    OID=1.3.6.1.5.5.7.3.1 ; Server Authentication
    
    [Extensions]
    2.5.29.17 = "{text}"
    _continue_ = "dns=www.example.com&"
    _continue_ = "dns=example.com&"
    "@
    
    $inf | Out-File -FilePath "C:\certs\request.inf" -Encoding ASCII
    
    # Generate CSR
    certreq -new "C:\certs\request.inf" "C:\certs\www.example.com.csr"

    Pro Tips:

    • Always include SANs (Subject Alternative Names) for modern certificates
    • Use KeySpec = 1 (AT_KEYEXCHANGE) for SSL certificates
    • Set Exportable = TRUE if you'll need to backup or migrate the certificate
    • Validate your CSR with FixMyCert CSR Checker before submitting to CA

    3. Installing the Certificate

    Scenario A: Completing a Pending Request

    Use this when the CSR was generated on this server and you received the signed certificate from your CA.

    IIS Manager Method:

    1. Server Certificates → Complete Certificate Request
    2. Browse to .cer/.crt file from CA
    3. Friendly name: Use descriptive naming (e.g., www.example.com-DigiCert-2027)
    4. Certificate store: Select Web Hosting (recommended) or Personal

    Certreq Method:

    certreq -accept "C:\certs\www.example.com.cer"

    Common Issue - "Cannot find the certificate request"

    The CSR creates a pending request tied to the machine. If the server was rebuilt or the request was lost, you cannot complete it.

    Workaround: If the CA won't easily reissue, import the issued cert into LocalMachine\My using MMC, then export as PFX with private key from another machine that has it.

    Scenario B: Importing a PFX File

    Use this when importing a certificate from another server or when you have a PFX/PKCS12 file.

    IIS Manager Method:

    1. Server Certificates → Import
    2. Browse to .pfx file
    3. Enter PFX password
    4. Critical: Check "Allow this certificate to be exported" if needed
    5. Select certificate store: Web Hosting (recommended)

    PowerShell Method:

    # Import PFX to Web Hosting store
    $pfxPassword = ConvertTo-SecureString -String "YourPassword" -Force -AsPlainText
    Import-PfxCertificate -FilePath "C:\certs\www.example.com.pfx" `
        -CertStoreLocation "Cert:\LocalMachine\WebHosting" `
        -Password $pfxPassword `
        -Exportable

    Scenario C: Installing Intermediate Certificates

    If your CA provides intermediate certificates, they must be installed for the chain to be complete.

    MMC Method:

    1. Run mmc.exe → Add Certificates snap-in → Computer account → Local computer
    2. Navigate to Intermediate Certification Authorities → Certificates
    3. Right-click → All Tasks → Import
    4. Import the intermediate certificate(s)

    Verify certificate chain:

    # Check certificate chain
    $cert = Get-ChildItem -Path "Cert:\LocalMachine\WebHosting" | 
        Where-Object { $_.Subject -like "*www.example.com*" }
    certutil -verify -urlfetch $cert.Thumbprint

    4. Creating HTTPS Bindings

    Single Certificate Binding

    IIS Manager:

    1. Sites → Select website → Bindings (Actions pane)
    2. Add → Type: https
    3. IP Address: All Unassigned (or specific IP)
    4. Port: 443
    5. SSL certificate: Select from dropdown
    6. Click OK

    PowerShell Method:

    # Get certificate thumbprint
    $cert = Get-ChildItem -Path "Cert:\LocalMachine\WebHosting" | 
        Where-Object { $_.Subject -like "*www.example.com*" }
    
    # Create HTTPS binding
    New-WebBinding -Name "Default Web Site" -Protocol https -Port 443 -IPAddress "*"
    
    # Assign certificate to binding
    $binding = Get-WebBinding -Name "Default Web Site" -Protocol https
    $binding.AddSslCertificate($cert.Thumbprint, "WebHosting")

    Server Name Indication (SNI) - Multiple Certificates on One IP

    SNI allows multiple HTTPS sites to share the same IP address, each with its own certificate. Available in IIS 8.0+ (Windows Server 2012+).

    SslFlags ValueMeaning
    0No SNI (traditional IP:Port binding)
    1SNI required
    2Use Centralized Certificate Store (CCS)
    3SNI + CCS

    PowerShell with SNI:

    # Create SNI binding
    New-WebBinding -Name "Site1" -Protocol https -Port 443 -HostHeader "site1.example.com" -SslFlags 1
    
    # Assign certificate
    $binding = Get-WebBinding -Name "Site1" -Protocol https -HostHeader "site1.example.com"
    $binding.AddSslCertificate($cert.Thumbprint, "WebHosting")

    Verifying Bindings

    View all SSL bindings in HTTP.sys:

    netsh http show sslcert
    
    # Output shows:
    # IP:port or Hostname:port
    # Certificate Hash
    # Application ID
    # Certificate Store Name

    SNI Browser Compatibility:

    • • Supported: All modern browsers (Chrome, Firefox, Edge, Safari)
    • • Not supported: IE on Windows XP (increasingly irrelevant)
    • • Consider having a fallback default certificate for legacy clients

    5. TLS Protocol and Cipher Hardening

    Windows Server defaults include legacy protocols (TLS 1.0, 1.1) and weak ciphers that fail security audits and SSL Labs testing.

    Legacy compatibility warning: Disabling TLS 1.0/1.1 may break:

    • Old SQL Server clients (pre-2016)
    • .NET Framework 3.5/4.0 applications without patches
    • Legacy load balancers and monitoring tools

    Always test in staging before production deployment.

    Method 1: IIS Crypto Tool (Recommended for Most Users)

    IIS Crypto is a free GUI tool from Nartac Software that modifies SChannel registry settings:

    1. Download IIS Crypto from nartac.com
    2. Run as Administrator
    3. Click "Best Practices" for secure defaults
    4. Review selections (ensures TLS 1.2/1.3 only, strong ciphers)
    5. Apply → Reboot server

    Method 2: PowerShell/Registry (For Automation)

    Disable Legacy Protocols:

    # Disable TLS 1.0
    New-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server" -Force
    New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server" -Name "Enabled" -Value 0 -PropertyType DWORD
    New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server" -Name "DisabledByDefault" -Value 1 -PropertyType DWORD
    
    # Disable TLS 1.1
    New-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server" -Force
    New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server" -Name "Enabled" -Value 0 -PropertyType DWORD
    New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server" -Name "DisabledByDefault" -Value 1 -PropertyType DWORD

    Enable Modern Protocols:

    # Enable TLS 1.2 (usually already enabled, but explicit)
    New-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server" -Force
    New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server" -Name "Enabled" -Value 1 -PropertyType DWORD
    New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server" -Name "DisabledByDefault" -Value 0 -PropertyType DWORD
    
    # Enable TLS 1.3 (Windows Server 2022+)
    New-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.3\Server" -Force
    New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.3\Server" -Name "Enabled" -Value 1 -PropertyType DWORD
    New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.3\Server" -Name "DisabledByDefault" -Value 0 -PropertyType DWORD

    Version note: The Get-TlsCipherSuite and Enable-TlsCipherSuite cmdlets require Windows Server 2016+ with recent updates. On older 2012/2012R2 systems, use IIS Crypto tool or direct registry edits instead.

    Configure Cipher Suite Order:

    # Get current cipher suites (Windows Server 2016+)
    Get-TlsCipherSuite | Format-Table Name
    
    # Recommended cipher suite order (TLS 1.2 + 1.3)
    $cipherSuites = @(
        "TLS_AES_256_GCM_SHA384",
        "TLS_AES_128_GCM_SHA256",
        "TLS_CHACHA20_POLY1305_SHA256",
        "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
        "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
        "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
        "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
    )
    
    # Disable weak cipher suites
    Disable-TlsCipherSuite -Name "TLS_RSA_WITH_AES_256_CBC_SHA256"
    Disable-TlsCipherSuite -Name "TLS_RSA_WITH_AES_128_CBC_SHA256"
    Disable-TlsCipherSuite -Name "TLS_RSA_WITH_3DES_EDE_CBC_SHA"
    
    # Enable strong cipher suites at preferred positions
    foreach ($cipher in $cipherSuites) {
        Enable-TlsCipherSuite -Name $cipher -Position 0
    }

    TLS 1.3 note: TLS 1.3 cipher suites (TLS_AES_*) are not configurable in the same way as TLS 1.2 ciphers. Windows manages TLS 1.3 ciphers automatically and registry/cmdlet changes for those suites may appear to do nothing. Focus your hardening efforts on TLS 1.2 cipher order and disabling weak ciphers there.

    Method 3: Group Policy (Enterprise Deployments)

    For domain-joined servers:

    1. Computer Configuration → Administrative Templates → Network → SSL Configuration Settings
    2. SSL Cipher Suite Order: Enable and set preferred order
    3. Apply to OU containing IIS servers

    Important Notes:

    • • Changes require server reboot (or net stop http /y then restart)
    • • TLS 1.3 requires Windows Server 2022 or later
    • • Some legacy applications may break without TLS 1.0/1.1 - test thoroughly

    6. Enabling HSTS (HTTP Strict Transport Security)

    HSTS forces browsers to always connect over HTTPS, preventing downgrade attacks.

    Native HSTS (IIS 10.0 version 1709+)

    IIS Manager:

    1. Select site → HSTS (in Features View)
    2. Enable → Set Max-Age (recommended: 31536000 = 1 year)
    3. Optional: Include subdomains, Redirect HTTP to HTTPS

    PowerShell Method:

    # Enable HSTS on a site
    Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST" `
        -Location "Default Web Site" `
        -Filter "system.applicationHost/sites/site/hsts" `
        -Name "enabled" -Value $true
    
    Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST" `
        -Location "Default Web Site" `
        -Filter "system.applicationHost/sites/site/hsts" `
        -Name "max-age" -Value 31536000
    
    Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST" `
        -Location "Default Web Site" `
        -Filter "system.applicationHost/sites/site/hsts" `
        -Name "redirectHttpToHttps" -Value $true

    HSTS via URL Rewrite (Older IIS Versions)

    web.config approach:

    <configuration>
      <system.webServer>
        <rewrite>
          <rules>
            <rule name="HTTP to HTTPS redirect" stopProcessing="true">
              <match url="(.*)" />
              <conditions>
                <add input="{HTTPS}" pattern="off" ignoreCase="true" />
              </conditions>
              <action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="Permanent" />
            </rule>
          </rules>
          <outboundRules>
            <rule name="Add HSTS Header" enabled="true">
              <match serverVariable="RESPONSE_Strict_Transport_Security" pattern=".*" />
              <conditions>
                <add input="{HTTPS}" pattern="on" ignoreCase="true" />
              </conditions>
              <action type="Rewrite" value="max-age=31536000; includeSubDomains" />
            </rule>
          </outboundRules>
        </rewrite>
      </system.webServer>
    </configuration>

    7. Centralized Certificate Store (CCS)

    What is CCS?

    • • Stores certificates in a file share instead of Windows certificate store
    • • Certificates loaded dynamically based on hostname
    • • Ideal for web farms and multi-tenant hosting
    • • Certificate file naming convention: hostname.pfx

    When NOT to use CCS:

    • Single small server with few certificates (overkill)
    • No shared storage infrastructure available
    • Team lacks ops maturity for shared credential management
    • Non-standard certificate workflows that CCS can't accommodate

    Setup Steps

    1. Enable CCS Feature:

    Install-WindowsFeature Web-CertProvider

    2. Create Certificate Share:

    • Create network share accessible by all IIS servers
    • Set NTFS permissions: IIS servers need read access
    • Share permissions: IIS machine accounts need read

    3. Configure CCS in IIS:

    # Enable Centralized Certificate Store
    Enable-WebCentralCertProvider -CertStoreLocation "\\fileserver\certs" `
        -UserName "DOMAIN\svc-iis-certs" `
        -Password (ConvertTo-SecureString "password" -AsPlainText -Force) `
        -PrivateKeyPassword (ConvertTo-SecureString "pfxpassword" -AsPlainText -Force)

    4. Create Binding with CCS:

    # SslFlags 2 = Use CCS
    # SslFlags 3 = SNI + CCS
    New-WebBinding -Name "Site1" -Protocol https -Port 443 -HostHeader "site1.example.com" -SslFlags 3

    CCS File Naming Requirements:

    • • Primary: www.example.com.pfx
    • • Wildcard: _.example.com.pfx (underscore replaces asterisk)
    • • All PFX files must use the same password

    8. Troubleshooting Common Issues

    Issue 1: "Cannot find the certificate request associated with this certificate file"

    Symptoms: Complete Certificate Request fails; certificate from CA cannot be installed

    Causes:

    • CSR generated on different machine
    • Pending request was deleted
    • Machine rebuilt since CSR was generated

    Solutions: Import via PFX instead, or have CA reissue certificate. Check certutil -store My for orphaned requests.

    Issue 2: Certificate Disappears After Binding

    Symptoms: Certificate shows in Server Certificates but disappears from SSL binding dropdown

    Diagnostic Commands:

    # Check for private key
    certutil -verifystore My "thumbprint"
    # Look for "Key Container" - if missing, need PFX with private key
    
    # Check KeySpec (should be 1 for SSL)
    certutil -v -store My "thumbprint"

    Solutions:

    • Missing private key: Re-import with PFX that includes private key
    • Wrong KeySpec (2 instead of 1): Certificate must be regenerated
    • MachineKeys folder permissions: Check C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys

    Issue 3: Error HRESULT: 0x80070520 When Adding Binding

    Symptoms: HTTPS binding creation fails; Event log shows SChannel error 36870

    Solutions:

    • Re-import certificate ensuring "Allow export" is checked
    • Use MMC import instead of IIS import
    • Corrupted keyset - may need to delete and reimport

    Issue 4: SSL Binding Exists But Site Won't Load

    Symptoms: Binding configured correctly but HTTPS connections fail or timeout

    # Check if HTTP.sys has the binding
    netsh http show sslcert
    
    # Check if port 443 is listening
    netstat -an | findstr :443
    
    # Restart HTTP service to refresh bindings
    net stop http /y
    net start http

    Solutions: Verify firewall allows 443, check Application ID matches IIS, restart HTTP.sys service

    Issue 5: "A specified logon session does not exist"

    Symptoms: Error when trying to assign certificate to binding; appears after server reboot

    Cause: Private key protected by user profile that's not loaded

    Solutions:

    • Re-import certificate with MachineKeySet = TRUE
    • Ensure certificate is in LocalMachine store, not CurrentUser
    • Grant IIS_IUSRS read access to the private key

    Issue 6: Certificate Chain Errors (Incomplete Chain)

    Symptoms: Browser shows "Certificate not trusted"; SSL Labs shows incomplete chain

    # Verify certificate chain
    $cert = Get-ChildItem "Cert:\LocalMachine\WebHosting" | Where-Object { $_.Subject -like "*example.com*" }
    certutil -verify -urlfetch $cert.Thumbprint
    
    # Check intermediate store
    Get-ChildItem "Cert:\LocalMachine\CA" | Format-Table Subject, Thumbprint

    Solutions:

    • Download and install intermediate certificates from CA
    • Import to Intermediate Certification Authorities store
    • Use file format guide to build complete chain

    9. PowerShell Reference Scripts

    Complete Certificate Deployment Script

    Idempotency note: This script removes any existing HTTPS binding on port 443 before creating a new one. If you have multiple HTTPS bindings with different host headers, modify the Get-WebBinding call to include -HostHeader $HostHeader to target the specific binding.

    <#
    .SYNOPSIS
        Deploys SSL certificate to IIS site with proper binding
    .PARAMETER PfxPath
        Path to the PFX certificate file
    .PARAMETER PfxPassword
        Password for the PFX file
    .PARAMETER SiteName
        IIS site name to bind certificate
    .PARAMETER HostHeader
        Hostname for SNI binding (optional)
    #>
    param(
        [Parameter(Mandatory=$true)]
        [string]$PfxPath,
        
        [Parameter(Mandatory=$true)]
        [SecureString]$PfxPassword,
        
        [Parameter(Mandatory=$true)]
        [string]$SiteName,
        
        [string]$HostHeader = ""
    )
    
    # Import certificate to Web Hosting store
    $cert = Import-PfxCertificate -FilePath $PfxPath `
        -CertStoreLocation "Cert:\LocalMachine\WebHosting" `
        -Password $PfxPassword `
        -Exportable
    
    Write-Host "Imported certificate: $($cert.Thumbprint)" -ForegroundColor Green
    
    # Remove existing HTTPS binding if present
    $existingBinding = Get-WebBinding -Name $SiteName -Protocol https -Port 443 -ErrorAction SilentlyContinue
    if ($existingBinding) {
        Remove-WebBinding -Name $SiteName -Protocol https -Port 443
        Write-Host "Removed existing HTTPS binding" -ForegroundColor Yellow
    }
    
    # Create new binding
    $sslFlags = if ($HostHeader) { 1 } else { 0 }  # 1 = SNI required
    if ($HostHeader) {
        New-WebBinding -Name $SiteName -Protocol https -Port 443 -HostHeader $HostHeader -SslFlags $sslFlags
    } else {
        New-WebBinding -Name $SiteName -Protocol https -Port 443 -IPAddress "*"
    }
    
    # Assign certificate to binding
    $binding = Get-WebBinding -Name $SiteName -Protocol https
    $binding.AddSslCertificate($cert.Thumbprint, "WebHosting")
    
    Write-Host "Certificate bound to $SiteName successfully" -ForegroundColor Green
    
    # Verify binding
    netsh http show sslcert | Select-String -Pattern $cert.Thumbprint

    Certificate Expiration Report Script

    Run this as a scheduled task (e.g., weekly) and pipe output to email, Teams, or Slack for proactive monitoring.

    <#
    .SYNOPSIS
        Reports on certificate expiration across all IIS bindings
    .PARAMETER DaysWarning
        Number of days before expiration to flag as warning (default: 30)
    .EXAMPLE
        # Run as scheduled task, export to CSV, send via email
        .Get-CertExpiration.ps1 | Export-Csv -Path C:Reportscerts.csv -NoTypeInformation
    #>
    param(
        [int]$DaysWarning = 30
    )
    
    $results = @()
    $warningDate = (Get-Date).AddDays($DaysWarning)
    
    # Get all certificates from Web Hosting and Personal stores
    $stores = @("WebHosting", "My")
    foreach ($store in $stores) {
        $certs = Get-ChildItem "Cert:\LocalMachine\$store" | 
            Where-Object { $_.HasPrivateKey -and $_.NotAfter -gt (Get-Date) }
        
        foreach ($cert in $certs) {
            $status = if ($cert.NotAfter -lt $warningDate) { "WARNING" } else { "OK" }
            
            $results += [PSCustomObject]@{
                Subject = $cert.Subject
                Thumbprint = $cert.Thumbprint.Substring(0, 8) + "..."
                Store = $store
                ExpiresOn = $cert.NotAfter.ToString("yyyy-MM-dd")
                DaysLeft = ($cert.NotAfter - (Get-Date)).Days
                Status = $status
            }
        }
    }
    
    # Display results
    $results | Sort-Object DaysLeft | Format-Table -AutoSize
    
    # Export to CSV
    $results | Export-Csv -Path "C:\certs\expiration-report.csv" -NoTypeInformation
    Write-Host "Report exported to C:\certs\expiration-report.csv" -ForegroundColor Green
    
    # Return certificates expiring soon for alerting
    $expiringCerts = $results | Where-Object { $_.Status -eq "WARNING" }
    if ($expiringCerts) {
        Write-Host "WARNING: The following certificates expire within $DaysWarning days:" -ForegroundColor Red
        $expiringCerts | Format-Table
    }

    10. SSL Labs A+ Grade Checklist

    Use this checklist to achieve an A+ rating on SSL Labs for your IIS server.

    RequirementHow to Achieve
    Valid certificate chainInstall all intermediate certificates in CA store. Verify with certutil -verify
    No SHA-1 certificatesEnsure certificate uses SHA-256 or higher. Check with certutil -v -store
    TLS 1.2/1.3 onlyDisable TLS 1.0, 1.1 via IIS Crypto or registry. Enable TLS 1.3 on Server 2022+
    No weak ciphersDisable CBC mode ciphers, 3DES, RC4. Use Disable-TlsCipherSuite
    Forward secrecyPrioritize ECDHE cipher suites. Remove RSA key exchange ciphers
    HSTS enabledEnable with max-age ≥ 31536000 (1 year). Use native HSTS or URL Rewrite
    No mixed contentEnsure all resources load over HTTPS. Use browser DevTools to identify issues
    OCSP staplingEnabled by default on Windows Server 2012+. Verify CA supports OCSP

    Quick Test:

    After configuration, test your site at SSL Labs Server Test. Aim for an A+ rating.

    Frequently Asked Questions

    Related Resources