TLS certificates on Windows: win-acme, IIS binding and renewal
Request certificates with win-acme, bind them to IIS, export PEM for Nginx, and prove renewal works before the old certificate expires.
- Build
- 26100.33650
- Edition
- Standard / Datacenter
- Architecture
- x64
- Support
- Documentation (E1)
- Evidence
- E1Official documentation
- Verified
- 2026-10-29
- Page type
- Task
The short answer
Section titled “The short answer”On Windows/IIS, win-acme is the practical ACME client:
- Create/renew a certificate.
- Install it into the Windows store.
- Bind it to the IIS site.
- Schedule renewal.
- Test renewal before expiry.
For Nginx, export PEM (fullchain.pem + privkey.pem) and reload Nginx after renewal.
Install win-acme
Section titled “Install win-acme”Download the x64 zip, extract to C:\win-acme, and run wacs.exe as Administrator. The interactive menu is the safest first run:
- N new certificate;
- choose Manual input or IIS site;
- select the site/host;
- choose HTTP-01 (port 80) or DNS-01 (wildcard/internal);
- choose store: Windows store / PEM files;
- install to IIS;
- create the renewal task.
CLI shape (verify against wacs --help for your version):
C:\win-acme\wacs.exe --target iis --host lab.example ` --installation iis --store certificatestoreDo not paste a CLI from an old blog without checking the current version’s options.
Bind to IIS
Section titled “Bind to IIS”If win-acme installs to IIS, it creates/updates the HTTPS binding. Verify:
Get-WebBinding -Name 'Lab' -Protocol httpsnetsh http show sslcert hostnameport=lab.example:443For SNI, use a hostname:port binding and keep SslFlags 1. An IP-only binding cannot serve multiple certificates on one IP.
Export PEM for Nginx
Section titled “Export PEM for Nginx”Nginx does not read the Windows store. Export:
- certificate + intermediates as
fullchain.pem; - private key as
privkey.pem; - ACL the key to the service account + Administrators.
Then:
ssl_certificate C:/nginx/certs/fullchain.pem;ssl_certificate_key C:/nginx/certs/privkey.pem;Reload Nginx after every renewal (nginx -s reload); replacing files without reload keeps the old cert in memory.
Verify renewal
Section titled “Verify renewal”Get-ChildItem Cert:\LocalMachine\My | Where-Object Subject -like '*lab.example*' | Select-Object Subject,NotAfter,ThumbprintCheck the renewal task exists and has run. Force a dry run/renewal in a maintenance window. A certificate that renews but does not rebind is still an outage.
Rate limits and wildcards
Section titled “Rate limits and wildcards”- Let’s Encrypt has rate limits; do not loop failed validations.
- Wildcards require DNS-01; HTTP-01 cannot issue
*.example.com. - DNS-01 credentials are powerful; store them with least privilege.
- Internal names cannot get public certificates; use an internal CA or
tls internal(Caddy lab).
Lab boundary
Section titled “Lab boundary”E1. The lab used a self-signed certificate for WAC and did not run win-acme; the commands are the production path to validate on Server 2025.