win-acme 证书续订排障笔记 Windows Server 2019 + XAMPP + Let's Encrypt

一、三个月后怎么自己续订(最重要)

第 1 步:看证书什么时候到期

管理员 CMD,一行:

C:\xampp\apache\bin\openssl.exe x509 -in C:\xampp\apache\conf\ssl\www.511yj.com-crt.pem -noout -dates

读 notBefore 和 notAfter。只要 notAfter 已经往后推了大约三个月,说明计划任务自己跑完了,什么都不用做。

顺手看一眼这张证覆盖哪些域名:

C:\xampp\apache\bin\openssl.exe x509 -in C:\xampp\apache\conf\ssl\www.511yj.com-crt.pem -noout -text | findstr /i "DNS:"

本次实测输出:DNS:511yj.com, DNS:bbs.511yj.com, DNS:www.511yj.com。三个域名在同一张 SAN 证书里,没有 home.511yj.com,bbs 不用单独申请、单独续。

第 2 步:看四个 PEM 有没有真的被覆盖

dir "C:\xampp\apache\conf\ssl\*.pem"

本次成功后四个文件的修改时间全部变成 2026/11/12 0:36。-crt.pem 和 -key.pem 由 wacs 成对写入,不用手工复制、不用手工导出 pfx。

第 3 步:看计划任务自己跑得怎么样

schtasks /query /tn "win-acme renew (acme-v02.api.letsencrypt.org)" /fo list /v | findstr /i /c:"上次运行时间" /c:"上次结果" /c:"下次运行时间"

上次结果 = 0x0 就是成功。若中文匹配不到,去掉 | findstr ... 后半段看完整输出;任务名以「任务计划程序」里显示的原名为准。

第 4 步:只有确认没续上,才手动跑一次

"C:\Program Files\Win-acme\wacs.exe" --renew

不带 --force。让 wacs 自己判断窗口,这是最不容易添乱的写法。看到 Renewal for [Manual] www.511yj.com succeeded 或 Certificate generated 就停。

第 5 步:确认 Apache 加载的是新证

这条 renewal 记录里配了安装脚本,成功时日志会打印 Script c:\xampp\apache\bin\httpd.exe starting with parameters -k restart,Apache 已自动重启。浏览器 Ctrl+F5 看「颁发日期」变今天即可。若没自动重启,XAMPP 控制台 Stop → Start 一次。

二、命令速查

目的 命令 怎么读结果
查证书到期 openssl.exe x509 -in ...\www.511yj.com-crt.pem -noout -dates notAfter 是否已推后约 90 天
查证书域名 openssl.exe x509 -in ...\www.511yj.com-crt.pem -noout -text | findstr /i "DNS:" 应列出三个域名
查 PEM 是否更新 dir "C:\xampp\apache\conf\ssl\*.pem" 四个文件时间是否变成今天
手动续订(推荐) "C:\Program Files\Win-acme\wacs.exe" --renew succeeded / 退出码 0x0
强制走一遍 "C:\Program Files\Win-acme\wacs.exe" --renew --force 只跳过「是否到期」判断,不保证成功
看详细报错 "C:\Program Files\Win-acme\wacs.exe" --renew --verbose 唯一可靠的定位依据
查计划任务 schtasks /query /tn "win-acme renew (acme-v02.api.letsencrypt.org)" /fo list /v 上次结果 / 下次运行时间
手动触发任务 schtasks /run /tn "win-acme renew (acme-v02.api.letsencrypt.org)" 「成功: 尝试运行」只代表调度器接受,不代表续期成功
终止卡住的任务 schtasks /end /tn "win-acme renew (acme-v02.api.letsencrypt.org)" 状态卡「正在运行」时用,之后 taskkill /im wacs.exe /f
读日志(PowerShell) Get-Content "C:\ProgramData\win-acme\acme-v02.api.letsencrypt.org\Log\log-20261111.txt" -Tail 45 看 [EROR] 和最后一行退出码
确认客户端完好 dir "C:\Program Files\Win-acme\wacs.exe" 约 39,725,880 字节;0 KB 就是被隔离了
确认 TCP 参数没被改回去 netsh interface tcp show global | findstr /i "ECN" 应为 disabled

三、退出码与报错对照表

看到 含义 动作
0x0 + No renewals are due 正常,还没到窗口 什么都不做
0x0 + succeeded 成功 查四个 PEM 日期
0xFFFFFFFF / -1 未处理异常 读日志,别猜
0xE0434352 .NET CLR 未处理异常 读日志
拒绝访问 进程根本没起来(常见于 exe 变 0 KB 或窗口不是管理员) dir 看文件大小
12002 / WinHttpException / 操作超时 出站请求超时 重跑,不改配置
'<' is an invalid start of a value /directory 超时后回退取根路径,把 1448 字节 Boulder HTML 当 JSON 解析 同一种超时的另一张脸
The operation has timed out + Mutex.ReleaseMutex 初始连接超时后清理时崩了一下,进程已退出 不影响下次,重跑
409 Conflict / alreadyReplaced ARI replaces 孤儿订单 把 Certificates 改名为 Certificates_bak,让 previous: [none]
Cached order has status invalid, discarding 自愈动作,丢弃旧订单 正常,继续看后面
Unable to refresh cached order 旧订单缓存刷新不了 可忽略;只要后面出现 Downloading certificate 就有戏
Source change in order Main detected 缓存被移走后判定源变化 正常,也是触发条件之一
challenge / Invalid / 404 才轮到验证文件问题 查 .well-known 和目录联接
Renewal ... succeeded 成了 查 PEM 日期、看浏览器颁发日期

四、环境与关键路径

项 值
系统 Windows Server 2019,10.0.17763.9487,阿里云 ECS,4 GB 内存
Web XAMPP / Apache + mod_php,WordPress 主站 + bbs 子站
客户端 win-acme 2.2.9.1701(release, pluggable, standalone, 32 位)
程序目录 C:\Program Files\Win-acme\
配置目录 C:\ProgramData\win-acme\acme-v02.api.letsencrypt.org\
renewal 记录 配置目录根下的 xHCGjFDQdU2ir5xCyUa9tQ.renewal.json,不在 Renewals\ 子目录
日志 ...\Log\log-YYYYMMDD.txt
证书缓存 ...\Certificates\(.pfx)
证书输出 Store 插件 PemFiles → c:\xampp\apache\conf\ssl\,四个 .pem
安装脚本 httpd.exe -k restart(续期成功自动重启 Apache)
证书域名 www.511yj.com / 511yj.com / bbs.511yj.com(同一张 SAN,Manual 模式)
旧证 2026/8/26 签发,2026/11/24 09:37 到期
本次新证 2026/11/12 0:36 落地,下次窗口 2027/1/5
计划任务 win-acme renew (acme-v02.api.letsencrypt.org),每天 09:00,随机延迟 4 小时,执行时限 2 小时
关键 settings RenewalDays: 55、RetryCount: 15、RetryInterval: 5、Proxy.Url: [System]、DefaultBaseUri: https://acme-v02.api.letsencrypt.org/

五、昨晚排查复盘:一条链,不是十几种病

起点是任务计划程序里天天 0xFFFFFFFF。前台跑一次 --renew --verbose,看到的是同一个进程里两种结果:

GET /directory  → 10 秒超时
GET /           → 10 秒超时
Initial connection failed, retrying with TLS 1.2 forced
GET /directory  → OK
Connection OK!
...
Constructing ACME protocol client...
Getting service directory...
WinHttpException 12002 操作超时
Renewal for [Manual] www.511yj.com failed
Exiting with status code -1

初始连通性检查有「强制 TLS 1.2」兜底能过,正式 ACME 客户端没有兜底,撞上 10 秒硬超时就退出。后面所有花式报错,都是这一处超时落在不同代码分支的样子。

逐层清障(按实际顺序)

  1. 端点:任务命令行里的 baseuri 改成 https://acme-v02.api.letsencrypt.org/。settings.json 里 DefaultBaseUri 也是 https。
  2. TLS:写了两条 DefaultSecureProtocols = 0x800(64 位与 Wow6432Node)。wacs 是 32 位,Wow6432Node 那条才是关键。之后初始 GET 不再动不动需要 forced 兜底。
  3. 代理:netsh winhttp show proxy → 「直接访问(没有代理服务器)」,set | findstr /i proxy 无输出,排除。
  4. 防护软件:云锁把 wacs.exe 加进「例外进程」后,出站立刻通了,日志第一次跑到 POST /acme/new-order。同一套防护还两次把 exe 清成 0 KB——表现为 CMD 直接「拒绝访问」。替换回 39,725,880 字节后恢复。
  5. 409 ARI:Conflict / cannot indicate an order replaces certificate ... which already has a replacement order。之前某次 POST new-order 已在 LE 侧建单,本地却判失败。把 Certificates 改名为 Certificates_bak,让 previous: [none],409 消失。
  6. 验证文件:bbs.511yj.com 是独立 DocumentRoot C:/xampp/htdocs/bbs,而 wacs 把 token 写在 C:/xampp/htdocs/.well-known/acme-challenge/。建目录联接让两边读到同一份文件:
    mklink /J "C:\xampp\htdocs\bbs\.well-known" "C:\xampp\htdocs\.well-known"

    自测 curl.exe -sL ... probe.txt 返回 bbs=200 size=4。注意 -L 必须加,否则只看到 301 跳转页误判为失败。

  7. 命令粘贴:多次把整段回显粘回命令行,出现过 --verbose 后面黏四遍路径、Microsoft Windows [版本 ...] 被当命令执行、Press <Enter> to close。一条一条贴,一次回车。
  8. 网络栈:nslookup 显示端点解析到 prod.api.letsencrypt.org → ca80a1...pacloudflare.com → 172.65.32.248(Cloudflare Anycast);IPv6 无全局路由、八个网络服务全 RUNNING、Winsock 目录全是 mswsock.dll、WinDefend / WdNisSvc STOPPED、内存 4 GB 剩约 447 MB 但 Pages/sec 不高。最后在 netsh interface tcp show global 里发现 ECN 功能 : enabled——2019 出厂是 disabled,是装完系统跑的那个优化 bat 改的。关掉:
    netsh interface tcp set global ecncapability=disabled

    之后 curl 采样里 connect=0.000000(SYN 一次都没建起来)消失了,但仍偶发 total=20s / code=000。

  9. 远程通道:一度把安全组 3389 入站删了,连接还活着只是因为已有 TCP 不会被掐断。补回后远程明显变顺。这跟出站续期无关,但让「本地传文件到 ECS」不再卡。

成功那次的输出

Connection OK!
Unable to refresh cached order: An error occurred while sending the request.
Downloading certificate [Manual] www.511yj.com
Store with PemFiles...
Exporting .pem files to c:\xampp\apache\conf\ssl
Installing with Script...
Script c:\xampp\apache\bin\httpd.exe starting with parameters -k restart
Script finished
Next renewal due after 2027/1/5
Renewal for [Manual] www.511yj.com succeeded

四个 PEM 的修改时间随后全部变成 2026/11/12 0:36。

六、为什么最后成功了(诚实版:没有定论)

能确定的三件事

  • 成功那次没有 new-order、没有 challenge、没有 HEAD new-nonce,只有 Downloading certificate。它不是当场跑完验证链,而是把一张 LE 侧已经存在的证书下载回来。
  • 之前有一轮跑到 [bbs.511yj.com] Preliminary validation looks good 之后才 Error submitting challenge answer (12002),接着 Deactivating pending authorization。最合理的推断是:那次请求其实到了 LE、验证通过、证也签了,只是回包在回程丢了,wacs 判失败退出,证书留在了服务端。这是推断,不是已证实的事实。
  • 前面那些修复确实都是必要前置:https 端点、TLS 1.2、云锁白名单、exe 恢复、409 绕开、junction、ECN 回默认。没有它们,连「下载」这一步都走不到。

为什么会有「运行一万次也这样」的感觉

一次完整续期是 directory ×2 → HEAD new-nonce → renewal-info → new-order → challenge ×3 → finalize → 下载证书,8–10 个串联请求,每个都独立碰那 10 秒。RetryCount: 15 只管状态轮询次数,管不了单次超时上限,而且超时异常直接退出、不走重试。单次成功率哪怕有八成,连乘十次整程也只剩一成左右。所以每次都死在不同一步,看着像冒不完的新病。

而「下载已签好的证书」只要两三步。最后一次恰好只需要这一步,于是成了。8/26 手动申请能成也是同理——交互式菜单会等、会重试,没有一次性退出的 10 秒硬上限。

不是「阿里云故意减速逼升级」

  • 没有受益方:阿里云不卖 Windows Server 升级、不卖 win-acme,这张证书还是 Let's Encrypt 免费的。
  • 策略性限速不会只卡一半:Connection OK! 出现过很多次、HEAD 200 / 3.5s、Boulder 页面能打开、还收到过 409 业务响应,最后下载证书一次就通。
  • 全程没有升级过任何东西:wacs 一直是 2.2.9.1701,系统一直是 17763,证书照样续上了。

更朴素的解释:LE 的 API 前端挂在 Cloudflare Anycast,国内 ECS 跨境访问它本来就时通时断;叠上优化 bat 打开的 ECN(SYN 更容易被中间设备静默丢),再叠上 wacs 写死的 10 秒超时,就成了这个症状。

七、一次性加固清单(做完就别再动)

项 做法 为什么
云锁文件防护白名单 目录级加 C:\Program Files\Win-acme\、C:\ProgramData\win-acme\、C:\xampp\apache\conf\ssl\,别只加单条「例外进程」 exe 两次被清成 0 KB 就是这层干的
ECN 保持 disabled netsh interface tcp show global | findstr /i "ECN" 恢复 2019 出厂值,别再跑那个优化 bat
bbs 目录联接保留 mklink /J "C:\xampp\htdocs\bbs\.well-known" "C:\xampp\htdocs\.well-known" 下次 HTTP-01 三个域名还要共用 token
Certificates_bak 留着别还原 不删、不改回 Certificates 还原会把 409 的 replaces 带回来
3389 入站别删 安全组里保留,来源限制成本地公网 IP 最稳 删了当前会话还能用,一断就回不去
两条 TLS 注册表保留 DefaultSecureProtocols = 0x800,含 Wow6432Node wacs 是 32 位,读的是这条

八、排障原则(比任何一条命令都值钱)

  1. 只看日志,不看退出码。0xFFFFFFFF 可能是任何一步挂了。
  2. 一行一条命令,一次回车。把整段回显粘回命令行是这次最大的自伤来源。
  3. 别 cd 进 C:\Program Files\Win-acme 敲命令。那个目录里出现过 cd / netsh / reg 三个 0 KB 垃圾文件。用绝对路径调用。
  4. 别连点「运行」。任务有「请勿启动新实例」,重复点击会被静默吞掉;状态卡「正在运行」先 schtasks /end 再 taskkill /im wacs.exe /f。
  5. 业务层没坏就别改业务层。.htaccess、vhosts、压缩、缓存、CSS、PHP、WebP、Apache 配置全程没动过;日志没走到 challenge 就不加 ACME 排除规则。
  6. 不要用「重新申请新证书」当解药。它走同一串请求、卡在同一处,还多一条 renewal 记录、可能撞限流。
  7. 最坏只是回到现状。旧证有效到到期那一刻,手动续期失败不会让站塌,窗口里还有几十次机会。

一句话收尾:不是找到一个开关修好了,而是把障碍逐层清掉之后,最后一次恰好只需要「下载」这一步。下次不用复刻昨晚,按第一节看一眼 notAfter、需要时跑一次 --renew 就够了。

分享到:

用微信扫一扫,分享给朋友


发布日期:

所属分类: SEO 网站运营 标签:   



下一篇:

没有了,已经是最新文章