IIS has no ACME client of its own, so certificate automation on Windows is handled by win-acme, a command-line client that requests the certificate, writes it into the Windows certificate store, updates the IIS binding and registers a scheduled task to renew it. This guide covers the case that most business environments face: issuing from a commercial certificate authority, which means the ACME account has to be created with External Account Binding credentials that your CA generates for you.
Automating this matters more than it used to. Under CA/Browser Forum ballot SC-081v3, publicly trusted TLS certificates have been capped at 200 days since March 15, 2026, falling to 100 days on March 15, 2027 and to 47 days on March 15, 2029. Renewal timing is therefore a setting worth checking rather than accepting, and there is a section on it below.
Before you begin
- Administrator access to the Windows Server, over RDP or locally. win-acme requires administrator privileges, so the PowerShell window you run it from has to be elevated.
- An IIS site with a binding for the host name you want on the certificate. This is what the IIS source plugin matches against when it works out which names to request, and without it the run finds nothing to do.
- Inbound TCP port 80 reaching this server for that host name, which is what HTTP-01 validation needs. See the note below about what actually answers the challenge, because it is not what most guides say.
- Your CA’s ACME directory URL, plus an EAB Key Identifier and an EAB HMAC Key generated in your CA account portal. Ask which HMAC algorithm the endpoint expects while you are there; it saves a confusing failure later.
- DNS already pointing at the server. The CA resolves the name and connects to whatever answers, so validation fails if the record still points somewhere else.
Step 1: Download and extract win-acme
- Open the win-acme releases page and pick the newest stable release. The project’s home page at win-acme.com carries the documentation.
- Each release publishes several archives. Choose the one matching your processor architecture, normally x64, and take the pluggable build rather than the trimmed one. The project’s own guidance is that the pluggable version is the one to get if you want to download or develop extra plugins, and the DNS validation plugins are exactly that. The pluggable archive is roughly 35 MB against 13 MB for the trimmed one, which is the quickest way to tell them apart.
- Extract the archive to a permanent location. win-acme recommends %programfiles%\win-acme, and the reason is specific: the scheduled task stores the path to the executable, so a temporary or user-profile folder means renewals stop working the moment the folder is cleaned up or the path changes.
- Open PowerShell as Administrator and run wacs.exe once with no arguments to confirm it starts. You should get the interactive menu.
The interactive menu is worth knowing about even though the rest of this guide uses arguments. Running wacs.exe on its own walks you through the same choices as prompts, which is useful the first time or when you want to see what plugins your build actually has.
Step 2: Request and install the certificate
Run this from the elevated PowerShell window, substituting your own host names, ACME directory URL, contact address and EAB credentials. The backtick at the end of each line is PowerShell’s line continuation character, and it only works when it is the very last character on the line, so watch for a trailing space if you retype it.
& "C:\Program Files\win-acme\wacs.exe" `
--source iis `
--host "example.com,www.example.com" `
--store certificatestore `
--installation iis `
--baseuri "https://acme.yourca.example/v2/DV" `
--emailaddress "[email protected]" `
--eab-key-identifier "YOUR_EAB_KEY_ID" `
--eab-key "YOUR_EAB_HMAC_KEY" `
--accepttos
If you would rather not deal with line continuation at all, put the whole thing on a single line and drop every backtick. What you must not do is mix the two, because a backtick followed by anything other than the end of the line changes how PowerShell reads the next character.
What each argument does:
--source iisreads your IIS bindings to work out which host names belong on the certificate. It is the source of the name list, not the validation mechanism; those are separate plugins in win-acme and it is a common mix-up. The older spelling--targetstill parses but is marked obsolete.--hostfilters those bindings down to the names you want, as a comma-separated list.--siteiddoes the same job by IIS site ID if you would rather select the whole site.--store certificatestoresaves the certificate into the Windows certificate store. On current Windows versions it lands in the WebHosting store rather than Personal, which is worth knowing before you go looking for it.--installation iiscreates or updates the HTTPS binding in IIS and points it at the new certificate.--baseuriis your CA’s ACME directory URL. Leave it out and win-acme uses its built-in default endpoint instead, which is not your commercial CA.--emailaddressis the contact address linked to the ACME account.--eab-key-identifierand--eab-keycarry the External Account Binding credentials. win-acme documents that the key must be base64url encoded, so paste it exactly as the CA presented it and do not re-encode it.--accepttosaccepts the CA’s terms of service, which is required for an unattended run.--verboseis not in the command above but is the first thing to add when something goes wrong. It prints the detailed log to the console.
One argument is missing from most write-ups and causes a failure that reads like a credentials problem when it is not. --eab-algorithm sets the HMAC algorithm used to sign the account binding. win-acme defaults to HS256 and also accepts HS384 and HS512. If your CA expects one of the other two, registration is rejected even though the key identifier and key are both correct, so add it when your CA specifies an algorithm:
--eab-algorithm HS512
What actually answers the validation request
This is worth two minutes because it decides how you troubleshoot a failed validation. When no validation plugin is specified, win-acme uses SelfHosting, and SelfHosting does not place a file in your website’s folder. It registers its own listener with the Windows HTTP stack for the path /.well-known/acme-challenge/ on port 80 and answers the CA directly, alongside IIS rather than through it.
Two practical consequences follow. First, your site’s document root, its permissions and any URL rewrite rules are irrelevant to the challenge, so there is no point hunting for a challenge file that was never written. Second, this only works because IIS and win-acme share port 80 through the same Windows component; if a non-Microsoft web server holds port 80, the listener cannot start and win-acme says so explicitly in its error.
If SelfHosting does not suit your setup, the two common alternatives are --validation filesystem, which writes a real challenge file into the site’s web root and therefore does depend on IIS serving that path, and one of the DNS validation plugins, which needs no inbound port at all and is the only route to a wildcard certificate. DNS plugins are the reason to download the pluggable build in Step 1.
Step 3: Confirm the binding in IIS
- Open Server Manager, then Tools > Internet Information Services (IIS) Manager.
- Expand your server, open Sites, select your site, and click Bindings in the Actions pane.
- You should see an https entry on port 443 with your certificate selected. If the site serves more than one host name, check that Require Server Name Indication is ticked on each HTTPS binding, otherwise only one of them can hold a certificate on the same IP and port.
Then confirm what the site actually presents, from a machine that is not the server. This prints the certificate the site is serving so you can check the subject, the issuer and the dates against what you just issued:
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates
For a check from outside your network, including whether the intermediate certificate is being served alongside the leaf, use the SSL Checker.
Step 4: Confirm the renewal task exists
win-acme registers a Windows scheduled task on its first successful run. List what it is managing with:
& "C:\Program Files\win-acme\wacs.exe" --list --baseuri "https://acme.yourca.example/v2/DV"
Passing --baseuri here is not optional bookkeeping. win-acme keeps a separate configuration directory per ACME server, under C:\ProgramData\win-acme\ in a subfolder named after the server, with Log and Certificates folders inside it. Omit the argument and the command reports on the default endpoint instead, which looks exactly like having no renewals configured at all.
The scheduled task is named for the same reason. Open Task Scheduler and look for a task called win-acme renew followed by your CA’s hostname in brackets. If you use two certificate authorities you will have two tasks, one per endpoint, which is expected rather than a duplicate.
Step 5: Check the renewal window against the certificate lifetime
Left alone, win-acme renews 55 days after the certificate was issued. That figure is a static schedule counted from issuance, not from the expiry date, and it is set by RenewalDays in settings.json.
In most cases you will never notice, because win-acme also uses ACME Renewal Information by default. ARI lets the certificate authority publish the window in which it wants each certificate renewed, and win-acme follows that in preference to its own schedule whenever the endpoint supports it. This is the mechanism that keeps automation working as lifetimes shrink, since the CA moves the window and the client follows.
The gap to be aware of is what happens when the CA does not support ARI. The static 55-day schedule then applies, with one floor under it that is easy to miss: RenewalMinimumValidDays is unset out of the box, and win-acme reads an unset value as 7 days, so a certificate is renewed once it has less than that left however far away the 55-day mark still is. That only bites on short lifetimes. At 200 days, and still at the 100-day cap in 2027, the 55-day schedule fires first and the floor never comes into it; at the 47-day cap in 2029 the floor is what triggers the renewal, and seven days is a thin margin to absorb one failed attempt. Edit settings.json next to wacs.exe, give RenewalMinimumValidDays a value comfortably inside your certificate’s lifetime, and the schedule stops being something you have to remember.
To prove renewal works rather than waiting to find out, force one:
& "C:\Program Files\win-acme\wacs.exe" --renew --force --baseuri "https://acme.yourca.example/v2/DV"
--force ignores both the certificate’s remaining validity and the preferred schedule, so use it for a one-off test rather than in the scheduled task. Certificate authorities apply rate limits, so do not loop on it.
When the run fails
Re-run the same command with --verbose first. The full log is also written to the Log folder inside the configuration directory for that ACME server, under C:\ProgramData\win-acme\.
- The account is rejected although the credentials look right. Check the HMAC algorithm with your CA and add
--eab-algorithm. Then check the key itself for whitespace at either end, and confirm you have not re-encoded a value that was already base64url. - Validation times out. Confirm inbound port 80 reaches the server for that host name, through the firewall and anything in front of it. A redirect from HTTP to HTTPS is fine, since the CA follows redirects, but a device that blocks port 80 outright is not.
- win-acme cannot start its listener. Something else holds port 80. Its own error names the likely cause, a non-Microsoft web server, because those do not share the port the way IIS does. Switch to
--validation filesystemor a DNS plugin. - The command finds no bindings. The names in
--hosthave to match IIS bindings that already exist. Check spelling, and remember that a site with only an https binding gives the source plugin nothing to match if you filtered by host type. - It worked once and then stopped. Check that the win-acme folder has not moved and that the scheduled task is still present and enabled. The task records the path to the executable, so relocating the folder breaks renewal silently.
- You want to rehearse without spending rate limit. Add
--test, which switches to the test endpoint. Certificates issued there are not publicly trusted, which is the point.
For error strings that come from the ACME protocol itself rather than from win-acme, such as badNonce, unauthorized or a CAA record refusing issuance, see our guide to fixing ACME SSL certificate errors.
Frequently Asked Questions
Pluggable, unless you are certain you will never need a plugin. The project’s guidance is that the pluggable version is the one to take if you want to download or develop extra plugins, and every DNS validation provider ships as a plugin. The trimmed archive is around 13 MB and the pluggable one around 35 MB, so the two are easy to tell apart on the releases page.
With --store certificatestore it goes into the Windows certificate store, and on current Windows versions the default destination is the WebHosting store rather than Personal. That catches people out when they open the certificates snap-in, look in Personal, and conclude the run failed. The renewal state and logs live separately, under C:\ProgramData\win-acme\ in a folder named after the ACME server.
You need an IIS binding for the host name, because that is what the IIS source plugin reads to decide which names go on the certificate. You do not need that binding to serve the challenge. The default SelfHosting validation registers its own listener for /.well-known/acme-challenge/ on port 80 and answers the CA itself, so your site’s folder, permissions and rewrite rules play no part. What has to be true is that inbound port 80 reaches the server for that name.
Only with DNS validation. No certificate authority will validate a wildcard over HTTP, so the HTTP-01 flow on this page cannot produce one. Use one of win-acme’s DNS validation plugins with an API credential for your DNS provider, which also removes the need for any inbound port. This is the case the pluggable build exists for.
--list show nothing when I know a certificate is configured? You almost certainly omitted --baseuri. win-acme keeps renewals in a separate configuration directory per ACME server, so a bare --list reports on the default endpoint rather than your CA’s, and an empty list there says nothing about the renewals stored under the endpoint you actually used.
By default, 55 days after issuance, unless the certificate authority publishes ACME Renewal Information, in which case win-acme follows the window the CA suggests. There is a floor as well, though a low one: RenewalMinimumValidDays is unset by default and win-acme reads an unset value as 7 days, so a certificate with less than that left is renewed whatever the 55-day schedule says. Raise it in settings.json if your CA does not support ARI and its certificates live for less than about two months.
Yes, and the procedure is otherwise identical. win-acme targets Let’s Encrypt out of the box, so you drop --baseuri along with the two EAB arguments, since Let’s Encrypt does not use External Account Binding. Everything else, the source, store, installation, scheduled task and renewal behaviour, is unchanged.
For more on the protocol behind all of this, see what the ACME protocol is and our ACME SSL certificates page. To install a certificate on IIS by hand instead, see our guides to generating a CSR on IIS and installing an SSL certificate on IIS.
Save 10% on SSL Certificates when ordering from SSL Dragon today!
Fast issuance, strong encryption, 99.99% browser trust, dedicated support, and 25-day money-back guarantee. Coupon code: SAVE10

