Windows 平台 SSL 证书:腾讯云自动更新

这份说明记录的是 Windows Server + nginx 的 SSL 证书自动更新思路。它不是用 acme.sh 重新签发证书,也不是通过 DNS TXT 记录做 ACME 验证;当前更贴近的实现是:

  1. 证书仍然在腾讯云 SSL 证书控制台签发、续期。
  2. Windows 定时任务每天运行一次 PowerShell 脚本。
  3. 脚本调用腾讯云 SSL API 下载已签发证书的 ZIP 包。
  4. 解压 ZIP,取出 Nginx 目录里的证书文件。
  5. 备份 nginx 当前证书,再复制新证书到 nginx 的 conf\key 目录。
  6. 执行 nginx.exe -t 检查配置,成功后 nginx.exe -s reload 热加载。

这样做的好处是:不需要手动登录腾讯云下载证书,也不需要每次手动替换 nginx 证书文件。证书真正续期完成后,服务器会在下一次定时任务里自动把新证书同步下来。

当前服务器检查结果

这次检查远端时,能看到服务器上有一个计划任务:

AutoUpdateTencentSSL

计划任务是每天运行一次,服务器证书目录里也能看到 2026-07-03 下载并替换过的证书文件,说明之前确实走过“下载腾讯云证书包并复制到 nginx”的流程。

不过也发现一个需要注意的问题:当前计划任务参数看起来被 Windows 命令行引号污染过,类似 /RU SYSTEM /RL HIGHEST 被拼进了 PowerShell 参数里,并且任务引用的 C:\ssl-auto\Update-TencentSsl.ps1 当前没有找到。后续如果要真正恢复自动更新,需要重新放回脚本,并重新注册一次计划任务。

目录建议

建议把自动更新相关文件统一放在:

C:\ssl-auto

推荐结构:

C:\ssl-auto
  Update-TencentSsl.ps1
  tencent-ssl.env.ps1
  logs\
  work\
    zips\
    extract\
    backup\

Update-TencentSsl.ps1 是自动更新脚本,tencent-ssl.env.ps1 是密钥和域名配置。密钥文件不要提交到 Git,也不要公开。

密钥配置

创建:

C:\ssl-auto\tencent-ssl.env.ps1

内容示例:

$TencentSecretId = "AKIDxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$TencentSecretKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

$NginxRoot = "C:\nginx-1.18.0"
$CertTargetDir = Join-Path $NginxRoot "conf\key"

$Certificates = @(
  @{
    Domain = "example.com"
    CertificateId = "cert-xxxxxxxx"
  },
  @{
    Domain = "www.example.com"
    CertificateId = "cert-yyyyyyyy"
  }
)

其中 CertificateId 是腾讯云 SSL 证书 ID。可以在腾讯云 SSL 证书控制台看到,也可以通过腾讯云 API/CLI 查询。

如果后续腾讯云续期后生成了新的证书 ID,需要同步更新这里的 CertificateId;如果要做到完全自动识别最新证书,可以再扩展脚本,用 DescribeCertificates 按域名查询最新已签发证书。

PowerShell 脚本

模板脚本放在同目录:

软件\Update-TencentSsl.template.ps1

部署到服务器时,把它复制为:

C:\ssl-auto\Update-TencentSsl.ps1

第一次建议手动运行:

powershell.exe -NoProfile -ExecutionPolicy Bypass -File "C:\ssl-auto\Update-TencentSsl.ps1" -EnvFile "C:\ssl-auto\tencent-ssl.env.ps1"

如果脚本能正常下载、解压、复制证书,并且 nginx reload 成功,再注册定时任务。

注册 Windows 定时任务

推荐使用 PowerShell 注册,少踩 schtasks 的引号坑:

$action = New-ScheduledTaskAction `
  -Execute "powershell.exe" `
  -Argument '-NoProfile -ExecutionPolicy Bypass -File "C:\ssl-auto\Update-TencentSsl.ps1" -EnvFile "C:\ssl-auto\tencent-ssl.env.ps1"'

$trigger = New-ScheduledTaskTrigger -Daily -At 3:00am

Register-ScheduledTask `
  -TaskName "AutoUpdateTencentSSL" `
  -Action $action `
  -Trigger $trigger `
  -User "SYSTEM" `
  -RunLevel Highest `
  -Force

查看任务:

Get-ScheduledTask -TaskName "AutoUpdateTencentSSL"
Get-ScheduledTaskInfo -TaskName "AutoUpdateTencentSSL"

手动跑一次任务:

Start-ScheduledTask -TaskName "AutoUpdateTencentSSL"

nginx 配置

nginx 里固定引用目标证书文件即可:

server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate     conf/key/example.com_bundle.crt;
    ssl_certificate_key conf/key/example.com.key;
}

脚本每次只负责把腾讯云 ZIP 包里的 nginx 证书复制成这两个固定文件名:

example.com_bundle.crt
example.com.key

nginx 配置不用跟着证书包文件名变化。

安全边界

  • 不要把 SecretIdSecretKey 写进公开脚本。
  • 不要把 C:\ssl-auto\tencent-ssl.env.ps1 提交到 Git。
  • 腾讯云 API 密钥建议使用子账号,并只授予 SSL 证书相关权限。
  • 证书替换前要先备份旧文件。
  • nginx.exe -t 失败时不能 reload,避免把线上 nginx 切到错误配置。
  • 日志里不要打印完整密钥。

常见问题

这和 ACME 自动续签有什么区别?

ACME 自动续签是服务器自己向 Let's Encrypt、ZeroSSL 等 CA 申请新证书;这里是腾讯云已经签发好证书,服务器只负责定时下载和安装。

为什么要每天跑?

每天跑一次成本很低,也能确保腾讯云续期完成后,服务器不用等太久就能拿到新证书。脚本可以通过证书内容 hash 判断是否变化,没有变化就跳过 nginx reload。

为什么不用手动复制?

手动复制最容易忘,也容易把 .key.crt 放错目录。脚本化之后,只要腾讯云证书正常续期,服务器端替换流程就是可重复、可检查的。

参考资料

  • 腾讯云 SSL API DownloadCertificate:下载证书接口会返回 ZIP 的 base64 内容。
  • 腾讯云 SSL API 文档:接口域名是 ssl.tencentcloudapi.com,版本为 2019-12-05