How to Set Up Let’s Encrypt SSL for IIS on Windows Server 2022

Let’s Encrypt is a free, automated certificate authority that issues Domain Validation (DV) SSL certificates valid for 90 days. On Windows Server 2022 with IIS, the most common tools for obtaining and auto-renewing Let’s Encrypt certificates are win-acme (WACS) and Certify The Web. This guide covers both tools, certificate binding in IIS, wildcard certificates with DNS challenges, and renewal management.

Prerequisites

Before setting up Let’s Encrypt, verify that your domain’s DNS A record points to the public IP of your Windows Server 2022 machine. The server must be reachable on port 80 (HTTP) from the internet for the default HTTP-01 ACME challenge, unless you plan to use DNS-01 challenge for wildcard certificates. IIS must be installed and the site must be running and accessible at the domain name you are requesting the certificate for.

# Verify IIS is running
Get-Service W3SVC

# Check the site bindings
Import-Module WebAdministration
Get-WebBinding | Format-Table -AutoSize

# Test external access (replace with your domain)
Test-NetConnection -ComputerName yourdomain.com -Port 80

Downloading win-acme (WACS)

win-acme (wacs.exe) is an open-source ACME client for Windows that integrates natively with IIS. It handles the HTTP-01 challenge automatically by creating a temporary file in the IIS web root, verifies ownership with Let’s Encrypt, downloads the certificate, binds it to the IIS site, and sets up a scheduled task for automatic renewal.

# Download latest win-acme release from GitHub
# https://github.com/win-acme/win-acme/releases
# Download the win-acme.v2.x.x.x.trimmed.zip (trimmed = no .NET bundled, requires .NET 8)
# Or download the pluggable zip for DNS plugins

# Extract to a permanent location
Expand-Archive -Path C:Downloadswin-acme.v2.2.9.1701.trimmed.zip `
               -DestinationPath C:Toolswacs

# Verify the executable
C:Toolswacswacs.exe --version

.NET 8 Desktop Runtime or ASP.NET Core Runtime 8 must be installed for the trimmed build. If .NET 8 is not present, download the “dotnet8” variant which bundles its own runtime.

Running WACS in Interactive Mode (Simple)

The simplest way to issue a certificate is through WACS interactive mode. Run wacs.exe as Administrator and follow the prompts:

# Run as Administrator
C:Toolswacswacs.exe

# Interactive menu options:
# N - Create certificate (simple for IIS)
# M - Create certificate (more options)
# R - Renew certificates
# A - Manage renewals
# Q - Quit

Choose option N (simple) and WACS will enumerate all IIS sites. Select your site, confirm the binding hostname, and WACS handles everything: HTTP challenge, certificate download, PFX import to the Windows Certificate Store, IIS HTTPS binding creation, and scheduled task registration. The entire process takes under a minute.

Unattended Certificate Request from Command Line

For automation and scripting, WACS supports fully unattended operation via command-line arguments. This is useful for provisioning scripts, Ansible playbooks, or CI/CD pipelines:

# Issue certificate for a single domain, bind to IIS site "Default Web Site"
C:Toolswacswacs.exe `
    --target iis `
    --siteid 1 `
    --installation iis `
    --emailaddress [email protected] `
    --accepttos

# Issue certificate for a specific hostname binding
C:Toolswacswacs.exe `
    --target iis `
    --host "yourdomain.com,www.yourdomain.com" `
    --installation iis `
    --emailaddress [email protected] `
    --accepttos

# Issue certificate with custom store location
C:Toolswacswacs.exe `
    --target iis `
    --host "yourdomain.com" `
    --store certificatestore `
    --certificatestore My `
    --installation iis `
    --emailaddress [email protected] `
    --accepttos

Auto-Renewal via Scheduled Task

WACS automatically creates a Windows Scheduled Task named “win-acme renewal ([email protected])” the first time it runs. This task runs daily and renews any certificate that is within 30 days of expiration (configurable). Because Let’s Encrypt certificates expire after 90 days, renewal typically happens around day 60.

# View the scheduled task
Get-ScheduledTask -TaskName "win-acme*"

# Check the scheduled task action and trigger
Get-ScheduledTask -TaskName "win-acme renewal*" | Select-Object -ExpandProperty Actions
Get-ScheduledTask -TaskName "win-acme renewal*" | Select-Object -ExpandProperty Triggers

# Manually trigger a renewal check (dry run - no changes)
C:Toolswacswacs.exe --renew --nocache --test

# Force renew all certificates regardless of expiry
C:Toolswacswacs.exe --renew --force

If you need to change the renewal threshold (default 30 days), edit the appsettings.json file in the WACS directory and set RenewalDays to your desired value. A setting of 45 days gives more buffer for troubleshooting failed renewals.

Certificate Binding in IIS

WACS binds the certificate automatically when using the IIS installation mode. To verify or manually update the binding in IIS Manager, go to Sites → Your Site → Bindings, and check the HTTPS binding on port 443. The certificate should show the Let’s Encrypt issuer. To bind manually with PowerShell:

# Find the certificate thumbprint for your domain
$thumbprint = (Get-ChildItem Cert:LocalMachineMy |
    Where-Object { $_.Subject -like "*yourdomain.com*" -and
                   $_.Issuer -like "*Let's Encrypt*" } |
    Sort-Object NotAfter -Descending |
    Select-Object -First 1).Thumbprint

Write-Host "Thumbprint: $thumbprint"
Write-Host "Expires: $((Get-ChildItem Cert:LocalMachineMy$thumbprint).NotAfter)"

# Bind the certificate to IIS HTTPS site binding
Import-Module WebAdministration
$binding = Get-WebBinding -Name "Default Web Site" -Protocol https
$binding.AddSslCertificate($thumbprint, "My")

# Or create a new HTTPS binding if none exists
New-WebBinding -Name "Default Web Site" `
    -Protocol https -Port 443 -HostHeader "yourdomain.com" -SslFlags 1

SAN Certificates (Multiple Domains)

A Subject Alternative Name (SAN) certificate covers multiple hostnames in a single certificate. WACS handles SAN certificates naturally when you specify multiple hostnames or when a site has multiple bindings:

# Issue a SAN certificate covering the apex domain and www subdomain
C:Toolswacswacs.exe `
    --target manual `
    --host "yourdomain.com,www.yourdomain.com,api.yourdomain.com" `
    --validation selfhosting `
    --installation iis `
    --siteid 1 `
    --emailaddress [email protected] `
    --accepttos

Wildcard Certificates with DNS Challenge

Wildcard certificates (*.yourdomain.com) require the DNS-01 ACME challenge—HTTP challenge cannot validate wildcards. DNS-01 requires creating a specific TXT record in your DNS zone. WACS supports automated DNS-01 via plugins for popular DNS providers.

# Download the DNS plugin for your provider from the WACS plugins page
# Example: win-acme.v2.x.x.x.pluggable.zip includes plugin support

# Request a wildcard certificate using Cloudflare DNS plugin
C:Toolswacswacs.exe `
    --target manual `
    --host "*.yourdomain.com,yourdomain.com" `
    --validation dnsscript `
    --dnscreatescript "C:ToolswacsScriptsCloudFlare.ps1" `
    --dnsdeletescript "C:ToolswacsScriptsCloudFlare.ps1" `
    --emailaddress [email protected] `
    --accepttos

# For manual DNS challenge (no automation - requires manual TXT record creation)
C:Toolswacswacs.exe `
    --target manual `
    --host "*.yourdomain.com" `
    --validation manual `
    --emailaddress [email protected] `
    --accepttos

When using manual DNS challenge, WACS pauses and tells you exactly which TXT record to create in your DNS zone (e.g., _acme-challenge.yourdomain.com). After you create the record and wait for it to propagate (use Resolve-DnsName to confirm), press Enter in WACS to continue. Wildcard certificates cannot be renewed fully automatically without a DNS API plugin.

# Verify DNS TXT record propagation before pressing Enter
Resolve-DnsName -Name "_acme-challenge.yourdomain.com" -Type TXT -Server 8.8.8.8

Certify The Web — GUI Alternative

Certify The Web is a commercial GUI application for Let’s Encrypt on Windows IIS. It has a free tier (up to 5 certificates) and is often recommended for users who prefer a graphical interface. Download from https://certifytheweb.com.

# After installing Certify The Web, the service runs as a Windows service
Get-Service CertifyTheWeb

# The GUI is launched from the desktop shortcut or:
Start-Process "C:Program FilesCertify The WebCertifyTheWebApp.exe"

# Certify The Web also has a CLI for scripting
"C:Program FilesCertify The WebCertifyTheWebApp.exe" `
    --cli renew

Certify The Web stores its configuration in a local SQLite database under %ProgramData%Certify. It supports IIS automatic binding, Azure DNS, Cloudflare DNS, Route53, and many other DNS providers natively via a plugin UI. It also supports deployment tasks—actions that run after a certificate is renewed, such as exporting the PFX to a custom path, restarting a service, or running a custom script.

Troubleshooting ACME HTTP-01 Challenges

The HTTP-01 challenge requires Let’s Encrypt servers to reach a specific URL on your domain: http://yourdomain.com/.well-known/acme-challenge/TOKEN. Common failure causes include a firewall blocking port 80 from the internet, URL rewrite rules redirecting .well-known requests (common in WordPress configurations), authentication requirements on the web root, and the domain not resolving to this server.

# Test that the challenge path is accessible without redirect
# Let's Encrypt uses plain HTTP (port 80) for this check
curl -v "http://yourdomain.com/.well-known/acme-challenge/test"

# Check if IIS has a redirect rule that catches .well-known
%windir%system32inetsrvappcmd.exe list config "Default Web Site" /section:system.webServer/rewrite/rules

# Exclude .well-known from redirect rules in web.config
# Add this condition to any HTTP-to-HTTPS redirect rule:
# 

Let’s Encrypt Rate Limits

Let’s Encrypt enforces rate limits to prevent abuse. The most important limits are: 50 certificates per registered domain per week, 5 duplicate certificates per week, 5 failed validation attempts per hostname per hour, and 300 new orders per account per 3 hours. During testing, always use the Let’s Encrypt staging environment to avoid consuming production quota:

# Use WACS staging server for testing (does not issue trusted certificates)
C:Toolswacswacs.exe `
    --baseuri "https://acme-staging-v02.api.letsencrypt.org/directory" `
    --target iis `
    --siteid 1 `
    --emailaddress [email protected] `
    --accepttos `
    --test

# Staging certificates are issued by a fake root CA and will show
# "untrusted" in browsers, but the validation process is identical
# to production - use this to test your entire setup first

Certificates issued against the staging server are not trusted by browsers but confirm your ACME workflow is correct. Once staging succeeds, run without the --baseuri override to issue a real trusted certificate from the production Let’s Encrypt API.