How to Set Up Let’s Encrypt SSL for IIS on Windows Server 2025
Let’s Encrypt is a free, automated certificate authority that issues 90-day Domain Validation (DV) TLS certificates. On Linux with Apache or nginx, Certbot handles the entire process; on Windows with IIS, the recommended equivalent is win-acme (formerly known as letsencrypt-win-simple), a fully automated ACME v2 client that integrates natively with IIS — creating HTTPS bindings, installing certificates into the Windows certificate store, and scheduling automatic renewal. This tutorial covers downloading and configuring win-acme on Windows Server 2025, validating domain ownership via the HTTP-01 challenge, handling renewal automatically, troubleshooting common validation failures, and using the DNS-01 challenge for wildcard certificates.
Prerequisites
- Windows Server 2025 with IIS 10 installed and a running HTTP site
- A registered domain name with a public DNS A record pointing to the server’s public IP address
- Inbound TCP ports 80 and 443 open in Windows Firewall and any upstream firewall/NAT
- PowerShell 5.1 or later running as Administrator
- Internet access from the server (Let’s Encrypt’s ACME servers must be able to reach your domain on port 80 for HTTP-01 validation)
Step 1: Downloading win-acme
win-acme is distributed as a self-contained ZIP archive. Download the latest release directly from GitHub:
# Create a permanent directory for win-acme (not a temp folder — the renewal task needs it)
$wacmeDir = "C:Toolswin-acme"
New-Item -ItemType Directory -Path $wacmeDir -Force
# Get the latest release download URL
$release = Invoke-RestMethod -Uri "https://api.github.com/repos/win-acme/win-acme/releases/latest"
$asset = $release.assets | Where-Object { $_.name -like "*pluggable*trimmed*win-x64*" } | Select-Object -First 1
# Download and extract
Invoke-WebRequest -Uri $asset.browser_download_url -OutFile "$env:TEMPwacs.zip"
Expand-Archive -Path "$env:TEMPwacs.zip" -DestinationPath $wacmeDir -Force
# Confirm the executable is present
Get-Item "$wacmeDirwacs.exe"
Keep win-acme in a stable location such as C:Toolswin-acme. The scheduled renewal task that win-acme creates references the executable path — moving it after setup breaks automatic renewal.
Step 2: Running the Interactive Wizard
win-acme’s interactive wizard handles most configuration decisions through a series of prompts. Launch it from an elevated PowerShell window:
cd C:Toolswin-acme
.wacs.exe --target iis
The wizard proceeds through the following steps:
- Choose site — lists all IIS sites. Select the number corresponding to your site (e.g., Default Web Site).
- Choose host names — displays all host names bound to the site. You can include all of them or select specific ones. For a single domain like
example.comandwww.example.com, include both so a single certificate covers them. - Validation method — select HTTP-01 via IIS. win-acme creates a temporary file in a
.well-known/acme-challenge/folder inside your site root that Let’s Encrypt’s servers will request to verify domain ownership. - Certificate store — select Windows Certificate Store (My / Personal), which is where IIS reads certificates from.
- Installation step — select Create or update IIS bindings. win-acme will create an HTTPS binding on port 443 and configure it to use the new certificate automatically.
- Email address — enter a valid email for expiry notifications from Let’s Encrypt (sent when renewal is about to fail, not on every renewal).
win-acme then requests the certificate, validates the challenge, installs the cert, and updates IIS — all within the same session.
Step 3: Unattended (Scripted) Certificate Request
For automation pipelines or multiple sites, use command-line arguments instead of the wizard:
.wacs.exe `
--target iis `
--siteid 1 `
--host "example.com,www.example.com" `
--validation selfhosting `
--certificatestore My `
--installation iis `
--emailaddress "[email protected]" `
--accepttos
Replace --siteid 1 with the IIS site ID for your site (Get-Website | Select-Object Name, Id to list them). The --accepttos flag accepts the Let’s Encrypt Terms of Service non-interactively.
Step 4: Verifying the Certificate and IIS Binding
# List certificates in the Personal store issued by Let's Encrypt
Get-ChildItem Cert:LocalMachineMy |
Where-Object { $_.Issuer -like "*Let's Encrypt*" } |
Select-Object Subject, NotAfter, Thumbprint
# Confirm the IIS HTTPS binding was created/updated
Get-WebBinding -Name "Default Web Site" | Format-Table Protocol, bindingInformation, certificateHash
In IIS Manager, navigate to your site > Bindings. You should see an HTTPS binding on port 443 with the Let’s Encrypt certificate selected. Test from a browser or with PowerShell:
$req = [Net.HttpWebRequest]::Create("https://example.com")
$req.GetResponse().StatusCode # Should return OK
Step 5: Automatic Renewal via Scheduled Task
win-acme registers a Windows scheduled task named win-acme renew (win-acme A.B.C.D) that runs daily. Let’s Encrypt certificates are valid for 90 days; win-acme renews them when fewer than 55 days remain.
# Verify the scheduled task exists
Get-ScheduledTask -TaskName "win-acme*" | Select-Object TaskName, State, LastRunTime, NextRunTime
# Manually trigger a renewal check (useful for testing)
.wacs.exe --renew --verbose
# Force renewal of all certificates regardless of expiry date
.wacs.exe --renew --force
The task runs as SYSTEM by default, which has sufficient permissions to update the Windows certificate store and IIS bindings. If your site runs under a different identity, ensure SYSTEM still has read access to the site’s root for HTTP-01 validation file placement.
Step 6: DNS-01 Challenge for Wildcard Certificates
Let’s Encrypt requires DNS-01 validation to issue wildcard certificates (e.g., *.example.com). This involves creating a TXT record in DNS instead of serving a file over HTTP — which also works for servers that cannot accept inbound port 80 traffic.
# Example using the Azure DNS plugin (requires Az.Dns PowerShell module and service principal)
.wacs.exe `
--target manual `
--host "*.example.com,example.com" `
--validation azure `
--azuresubscriptionid "00000000-0000-0000-0000-000000000000" `
--azuretenantid "00000000-0000-0000-0000-000000000001" `
--azureclientid "00000000-0000-0000-0000-000000000002" `
--azureclientsecret "YourClientSecretHere" `
--azureresourcegroupname "dns-rg" `
--certificatestore My `
--installation iis `
--emailaddress "[email protected]" `
--accepttos
Other supported DNS providers include Cloudflare, Route53, GoDaddy, and generic RFC 2136 TSIG DNS update. For providers without a built-in plugin, the --validation script option lets you call a custom PowerShell script to create and clean up the TXT record:
.wacs.exe `
--target manual `
--host "*.example.com" `
--validation script `
--validationscript "C:Scriptsdns-create.ps1" `
--validationscriptdelete "C:Scriptsdns-delete.ps1"
Step 7: Troubleshooting Validation Errors
The most common win-acme failures and their solutions:
- HTTP 404 during challenge — Let’s Encrypt cannot retrieve the validation file. Check that
.well-knownacme-challengeis not blocked by a web.config<authorization>rule or a URL Rewrite rule that redirects all HTTP to HTTPS before the challenge file can be read. Add a rewrite exclusion:
<!-- In web.config: exclude ACME challenge from HTTP→HTTPS redirect -->
<rule name="Redirect to HTTPS" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTPS}" pattern="off" />
<add input="{REQUEST_URI}" pattern="^/.well-known/acme-challenge/" negate="true" />
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}/{R:1}" />
</rule>
- Rate limit exceeded — Let’s Encrypt limits duplicate certificate requests to 5 per week per registered domain. Use the Let’s Encrypt staging environment (
--baseuri "https://acme-staging-v02.api.letsencrypt.org/directory") for testing. - Certificate not appearing in IIS binding — run
.wacs.exe --renew --forceand check%ProgramData%win-acmeLogfor error details. - TLS handshake errors after installation — ensure TLS 1.2 and 1.3 are enabled in Schannel and the server supports the cipher suites in the Let’s Encrypt certificate chain.
Conclusion
win-acme transforms Let’s Encrypt certificate management on Windows Server 2025 into a near-zero-maintenance operation. After the initial setup — roughly ten minutes using the interactive wizard — certificate requests, IIS binding updates, and 90-day renewals are handled automatically by a Windows scheduled task. Wildcard certificates extend coverage to subdomains via DNS-01 validation, and the extensive plugin ecosystem supports nearly every DNS provider. The result is a fully trusted TLS configuration at no cost, with renewal handled before certificates expire, eliminating the risk of outage caused by a forgotten manual renewal.