acme.sh 自动申请并续签 SSL 证书的工具

摘要

acme.sh 介绍

  • acme.sh 实现了 acme 协议,可以从 ZeroSSL,Let’s Encrypt 等 CA 生成免费的证书。

  • 适用场景:shell脚本方式,几乎无依赖,极简,适合不想安装Python环境的人。

  • 特点:

    • 纯 Shell 脚本,单文件运行
    • 支持 100+ DNS API 自动续签(如 Cloudflare、阿里云、腾讯云等)
    • 支持通配符证书

acme.sh 安装

  • 需要先安装 crontab。以下以 RHEL/Fedora/CentOS 系为例;Debian/Ubuntu 可使用 apt install cron 并启动 cron 服务,macOS 通常已内置 cron。

1
2
3
4
5
6
7
8
# 安装 crontab(RHEL 系)
sudo dnf install cronie -y
# 启动 crond
sudo systemctl enable --now crond
# 查看 crond 状态
sudo systemctl status crond
# 查看 crontab 是否正常
crontab -l
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 安装成功后会自动安装到 ~/.acme.sh/ 目录下,证书也会生成在该目录下
curl https://get.acme.sh | sh -s email=qunfeng_han@aliyun.com

# 添加 alias(按实际 shell 选择配置文件:bash 用 ~/.bashrc,zsh 用 ~/.zshrc)
echo 'alias acme.sh=~/.acme.sh/acme.sh' >> ~/.bashrc
source ~/.bashrc

# 建议将默认 CA 切换为 Let's Encrypt,避免 ZeroSSL 偶发 Pending 问题
acme.sh --set-default-ca --server letsencrypt

# 安装成功后会自动在 crontab 中添加定时任务(每天检查,满足条件才续签,详见下文「自动续签机制」)
crontab -l
# 输出类似于以下内容
48 1 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null

# 若未自动添加 cron,可手动安装
acme.sh --install-cronjob

# 查看帮助
acme.sh -h
# 查看版本
acme.sh -v

升级 acme.sh

1
2
3
4
5
6
7
8
# 升级 acme.sh 到最新版
acme.sh --upgrade

# 开启自动升级
acme.sh --upgrade --auto-upgrade

# 可以随时关闭自动升级
acme.sh --upgrade --auto-upgrade 0

卸载 acme.sh

1
2
3
4
5
# --uninstall 会移除 cron 任务和 ~/.acme.sh 目录
acme.sh --uninstall

# 若目录仍存在,可手动删除
rm -rf ~/.acme.sh

生成证书

  • acme.sh 实现了 acme 协议支持的所有验证协议。

  • 证书创建后会保存在 ~/.acme.sh/ 目录下,比如我的域名是 acme.hanqunfeng.com,则证书保存在 ~/.acme.sh/acme.hanqunfeng.com_ecc/ 目录下

  • 创建证书时一般有两种方式验证: HTTP 和 DNS 验证。

HTTP 验证

  • 支持多个子域名(通过多次 -d 指定),但不支持通配符域名

  • 需要提前将域名解析到本机的 IP 地址

直接签发

  • 只需要指定域名,并指定域名所在的网站根目录。acme.sh 会全自动的生成验证文件,并放到网站的根目录,验证完成后会删除验证文件,整个过程没有任何副作用。

1
2
3
4
5
# -d 指定域名,可以添加多个,--webroot 指定网站根目录
acme.sh --issue -d acme.hanqunfeng.com -d www.hanqunfeng.com --webroot /home/wwwroot/mydomain.com/

# --server 指定 CA 服务商为 Let's Encrypt(默认是 ZeroSSL)
acme.sh --issue -d acme.hanqunfeng.com -d www.hanqunfeng.com --webroot /home/wwwroot/mydomain.com/ --server letsencrypt

使用 Nginx/Apache 模式

  • 如果你用的 Nginx/Apache 服务器,或者反代,acme.sh 还可以智能的从 Nginx/Apache 的配置中自动完成验证,你不需要指定网站根目录

  • nginx 或 httpd 命令要在系统 Path 中

1
2
3
4
# -d 域名,可以添加多个, --nginx 告诉 acme.sh 使用 Nginx 模式
acme.sh --issue --nginx -d acme.hanqunfeng.com -d www.hanqunfeng.com
# -d 域名,可以添加多个, --apache 告诉 acme.sh 使用 Apache 模式
acme.sh --issue --apache -d acme.hanqunfeng.com -d www.hanqunfeng.com

使用独立服务模式

  • 如果服务器上没有运行任何 Web 服务,80 端口是空闲的,那么 acme.sh 还能临时监听 80 端口,完成验证

  • 需要先安装 socat 命令,socat 常用于临时开启 TCP/UDP 监听端口

1
sudo dnf install socat -y
  • 生成证书,要求 80 端口空闲,否则会失败

1
2
# --standalone 独立服务模式
acme.sh --issue --standalone -d acme.hanqunfeng.com -d www.hanqunfeng.com

DNS 验证

  • 如果你没有服务器,没有公网 IP,只需要 DNS 的解析记录即可完成验证。

  • 支持通配符域名(通配符需加引号,防止 shell 展开 *

手动验证

  • 这需要你手动在域名上添加 TXT 解析记录,验证域名所有权。

  • 注意:手动验证无法自动续签。每次续签(含首次 --renew)都需要按提示重新添加新的 TXT 记录。生产环境请优先使用 DNS API 自动验证。

1
2
# 需要加上 --yes-I-know-dns-manual-mode-enough-go-ahead-please 选项
acme.sh --issue --dns -d acme.hanqunfeng.com -d www.hanqunfeng.com --yes-I-know-dns-manual-mode-enough-go-ahead-please
  • 然后,acme.sh 会生成相应的解析记录显示出来,你只需要在你的域名管理面板中添加这条 TXT 记录即可。

1
2
3
4
5
6
7
Add the following txt record:
Domain:_acme-challenge.acme.hanqunfeng.com
Txt value:mUWNg9kuQ9hwOkqYFQ_DFMQ4Eu0CEaxxxxxxxxxxx

Add the following txt record:
Domain:_acme-challenge.www.hanqunfeng.com
Txt value:vLwDR48eHcmcScOwHrDjaFZo-yw_f9xxxxxxxxxxx
  • 等待解析生效后,执行以下命令完成签发:

1
2
3
# 续签/完成签发时需同样带上确认参数
acme.sh --renew -d acme.hanqunfeng.com -d www.hanqunfeng.com \
--yes-I-know-dns-manual-mode-enough-go-ahead-please

自动验证(DNS API)

  • DNS 方式的真正强大之处在于可以使用域名解析商提供的 API 自动添加 TXT 记录,且在完成验证后删除对应的记录。

  • acme.sh 目前支持超过一百家的 DNS API

  • 以阿里云为例,登录阿里云帐号,获取 AccessKey 和 SecretKey,并设置环境变量:

1
2
3
# 只需要命令行执行一次,运行生成证书命令时会保存在 ~/.acme.sh/account.conf 中,并在需要时自动获取,无需手动再设置
export Ali_Key="<key>"
export Ali_Secret="<secret>"
  • 生成证书

1
2
# --dns dns_ali 指定阿里云的 DNS API;通配符域名需加引号
acme.sh --issue -d acme.hanqunfeng.com -d '*.hanqunfeng.com' --dns dns_ali

生成证书的其它说明

1
2
# 这里使用 Let's Encrypt 的 CA 服务商
acme.sh --issue -d acme.hanqunfeng.com -d '*.hanqunfeng.com' --dns dns_ali --server letsencrypt
  • 也可以设置全局默认的 CA 服务商,这样就不需要每次都指定 --server

1
acme.sh --set-default-ca --server letsencrypt

小贴士

  • 部分用户反馈默认 CA ZeroSSL 偶发不稳定,获取证书时可能长时间停留在:Pending,The CA is processing your order,please just wait.。可尝试切换为 Let’s Encrypt(--server letsencrypt--set-default-ca),通常重试 2-3 次即可成功。
  • Let’s Encrypt 默认证书链使用 ISRG Root X1,Android 7.1.1 以下等较老系统可能无法信任。若有老设备兼容需求,可考虑 ZeroSSL(链到 USERTrust RSA,对老 Android 更友好)。注意:Android 5.1 及更早设备可能还需在服务端配置 ZeroSSL 交叉签名证书链;ZeroSSL 免费版另有 90 天内 3 张证书 的限制。
  • 常见根证书表(仅供参考,实际兼容性以 SSL Labs 检测结果为准)
根证书(CN) 较老 Android 常见签发方 备注
ISRG Root X1 ❌ 不兼容 Let’s Encrypt Android ≥ 7.1.1 才默认信任
DST Root CA X3 ✅ 兼容(已过期) Let’s Encrypt 老版本 2021 年过期,仅历史参考
GlobalSign Root R1 ✅ 兼容 GlobalSign
USERTrust RSA Certification Authority ✅ 兼容 ZeroSSL, Sectigo 老设备友好
Starfield Root CA - G2 ❌ 不兼容 GoDaddy 新版
Starfield Root CA - G1 ✅ 兼容 GoDaddy 老版
GTS Root R1/R3 ❌ 较老系统不兼容 Google Trust Services
Amazon Root CA 1 ❌ 较老系统不兼容 Amazon Trust
  • 可用 SSL Labs 查看完整证书链和各设备兼容性

  • 查看已经生成的证书(Renew 列为预计续签时间,详见「自动续签机制」)

1
acme.sh --list
  • 如果生成证书时失败,可以通过添加 --debug 参数查看详细错误信息

1
2
3
acme.sh --issue -d acme.hanqunfeng.com -d '*.hanqunfeng.com' --dns dns_ali --server letsencrypt --debug
# --debug 2 输出更为详细的信息
acme.sh --issue -d acme.hanqunfeng.com -d '*.hanqunfeng.com' --dns dns_ali --server letsencrypt --debug 2
  • 自动续签的触发条件、前提和排查方式,见下文「自动续签机制」一节。

自动续签机制

acme.sh 的自动续签不是「每天自动换一张新证书」,而是 cron 每天检查一次,只有满足条件时才会真正执行续签。

前提条件

以下情况才会进入自动续签流程:

条件 说明
cron 任务存在且正常运行 安装时自动添加,也可用 acme.sh --install-cronjob 补装;crontab -l 应能看到 --cron 条目
证书仍由 acme.sh 管理 未对该域名执行 acme.sh --remove
验证方式支持无人值守 HTTP 模式(webroot / nginx / apache / standalone)、DNS API 模式(如 dns_ali)均支持
续签时验证环境仍可用 webroot 路径未变、80 端口可达(standalone)、DNS API 密钥仍有效等

以下情况不会自动续签:

情况 说明
DNS 手动验证 每次续签都需手动添加新的 TXT 记录,cron 只会报错提醒
cron 缺失或服务未运行 证书到期前不会有任何自动操作
已执行 --remove 该域名已从 acme.sh 续签列表中移除
续签时验证失败 如 DNS API 密钥失效、webroot 目录不存在、80 端口被占用等

什么时候会真正续签

cron 每天执行 acme.sh --cron,对每个已管理的证书逐一判断。满足以下任一条件即触发续签:

  1. 到达下次续签时间:默认在证书剩余约 30 天时续签(90 天有效期的证书,大约在第 60 天触发)。该时间保存在域名配置文件的 Le_NextRenewTime 中。

  2. CA 的 ARI 建议窗口已开始:Let’s Encrypt、ZeroSSL 等 CA 支持 ARI(RFC 9773) 时,可能按 CA 公布的 suggestedWindow 提前续签(无需额外配置,acme.sh 自动遵循)。

  3. 手动强制:执行 acme.sh --renew -d <域名> --force

简言之:平时不动作,到了续签窗口(或 CA 要求提前)才续签

查看下次续签时间

1
2
3
4
5
# --list 的 Renew 列即预计续签时间
acme.sh --list

# 也可直接查看域名配置文件
grep Le_NextRenewTime ~/.acme.sh/acme.hanqunfeng.com_ecc/acme.hanqunfeng.com.conf

示例输出(--list):

1
2
3
Main_Domain          KeyLength  SAN_Domains  CA           Created               Renew
acme.hanqunfeng.com "ec-256" no ZeroSSL.com 2025-07-17T03:31:30Z 2025-09-14T03:31:30Z
# ↑ 预计在此时间附近续签

续签成功后会发生什么

  1. 更新 ~/.acme.sh/<域名>_ecc/ 下的证书文件(.cer.keyfullchain.cer 等)。

  2. 若之前配置过 acme.sh --install-cert,会自动将新证书复制到 Nginx/Apache 指定路径,并执行 --reloadcmd 重载服务。

  3. 重新计算并写入下一次续签时间 Le_NextRenewTime

注意:--install-cert 中的 reloadcmd 非常重要。证书可以自动续签,但如果没有正确的重载命令,Web 服务可能仍在使用旧证书。

手动强制续签

未到续签时间也可手动触发:

1
2
# --issue 仅用于首次签发;续签用 --renew。--force 跳过时间检查
acme.sh --renew -d acme.hanqunfeng.com -d '*.hanqunfeng.com' --force

续签失败时排查

1
2
3
4
5
# 模拟 cron 执行,查看详细输出
acme.sh --cron

# 对单个域名调试续签
acme.sh --renew -d acme.hanqunfeng.com --force --debug 2

常见原因:cron 未安装、DNS API 密钥过期、webroot 路径变更、standalone 模式下 80 端口被占用。

生成证书后自动部署和更新

  • 上面无论是 http 还是 dns 模式,生成证书后,都会在 ~/.acme.sh/acme.hanqunfeng.com_ecc/ 目录下生成以下文件:

1
2
3
4
5
6
7
├── acme.hanqunfeng.com.cer
├── acme.hanqunfeng.com.conf
├── acme.hanqunfeng.com.csr
├── acme.hanqunfeng.com.csr.conf
├── acme.hanqunfeng.com.key
├── ca.cer
└── fullchain.cer
  • 我们可以手动将证书拷贝到真正使用证书的目录下。如果使用 Nginx 或 Apache,可以让 acme.sh 自动将证书拷贝到指定目录,并在续签后重载服务。以下命令只需运行一次,后续由 cron 自动完成更新和部署。

  • Nginx

参数说明:-d 为签发时的域名;--key-file 对应 Nginx 配置的私钥路径;--fullchain-file 对应证书链路径;--reloadcmd 为证书更新后的重载命令(也可用 nginx -s reload)。

1
2
3
4
5
acme.sh --install-cert \
-d acme.hanqunfeng.com \
--key-file /path/to/keyfile/in/nginx/key.pem \
--fullchain-file /path/to/fullchain/nginx/cert.pem \
--reloadcmd "systemctl reload nginx"
  • 文件对应关系

acme.sh 文件 含义 你配置的目标文件
acme.hanqunfeng.com.key 私钥 (Private Key) /path/to/keyfile/in/nginx/key.pem
fullchain.cer 证书 + 中间证书链 (Fullchain) /path/to/fullchain/nginx/cert.pem
  • Apache

参数说明:--cert-file 为域名证书;--key-file 为私钥;--fullchain-file 为完整证书链;--reloadcmd 为重载命令。

1
2
3
4
5
6
acme.sh --install-cert \
-d acme.hanqunfeng.com \
--cert-file /path/to/certfile/in/apache/cert.pem \
--key-file /path/to/keyfile/in/apache/key.pem \
--fullchain-file /path/to/fullchain/certfile/apache/fullchain.pem \
--reloadcmd "systemctl reload httpd"
  • 文件对应关系

acme.sh 文件 含义 你配置的目标文件
acme.hanqunfeng.com.key 私钥 (Private Key) /path/to/keyfile/in/apache/key.pem
acme.hanqunfeng.com.cer 仅域名证书 (Certificate) /path/to/certfile/in/apache/cert.pem
fullchain.cer 证书 + 中间证书链 (Fullchain) /path/to/fullchain/certfile/apache/fullchain.pem

移除证书并停止自动续签

1
2
3
4
5
# 从 acme.sh 管理中移除该域名(含续签配置和 cron 中的自动续签)
acme.sh --remove -d acme.hanqunfeng.com --ecc

# 若目录仍存在,可手动删除证书文件
rm -rf ~/.acme.sh/acme.hanqunfeng.com_ecc

其它ssl自动续签工具

工具 推荐场景 官网地址 依赖
Certbot 传统服务器、Nginx/Apache https://certbot.eff.org/ Python
Lego 静态二进制、K8s https://github.com/go-acme/lego 无依赖
cert-manager K8s 集群 https://cert-manager.io/ K8s CRD
Caddy 简单站点自动HTTPS https://caddyserver.com/ 无需单独工具
Traefik 微服务网关 https://traefik.io/ Docker/K8s