引言
acme.sh 是一个完全用纯 Shell (Unix shell语言) 编写的开源脚本工具。它实现了 ACME 协议(一种用于自动化获取和管理 SSL 证书的互联网协议),充当了用户与证书颁发机构(CA)之间的“自动化中介”。
简单来说,它把原本繁琐、需要人工干预的 SSL 证书申请和部署过程,变成了一键自动化操作。它的核心作用包括:
- 自动申请免费证书:能够自动向 Let's Encrypt、ZeroSSL 等受信任的证书颁发机构申请免费的 SSL/TLS 证书。
- 自动化域名验证:支持 HTTP 验证和 DNS API 验证。通过对接阿里云、腾讯云、Cloudflare 等 100 多家 DNS 服务商的 API,它可以自动添加 TXT 记录来完成域名所有权验证,无需手动操作。
- 自动部署与平滑重载:申请成功后,它能自动将证书复制到 Nginx 或 Apache 等 Web 服务器的指定目录,并自动执行重载命令(如
nginx -s reload),实现零停机更新。 - 自动续期(核心优势):安装后会自动创建 Cron 定时任务,默认每 60 天检查一次证书状态。在证书到期前,它会自动完成续签、部署和重载的全套流程,彻底解放运维人员的双手。
- 支持泛域名证书:只需一条简单的命令,就能为
*.example.com及其所有子域名签发一张通配符证书,极大地简化了多子域名站点的管理。 - 轻量且零依赖:由于是纯 Shell 编写,它不需要 Python 等复杂的运行环境,不污染系统文件,几乎可以在所有主流的 Unix-like 系统(Linux、macOS 等)上直接运行。
产品对比
| 特性 | certd | certbot | acme.sh |
|---|---|---|---|
| 定位 | 国产证书自动化管理平台 | Let's Encrypt 官方客户端 | 社区流行的轻量 Shell 脚本 |
| 协议支持 | ACME v2 | ACME v2 | ACME v2 |
| 证书来源 | Let's Encrypt、ZeroSSL、Google Trust 等 | Let's Encrypt(官方) | Let's Encrypt、ZeroSSL、Google Trust、Buypass 等 |
| 部署方式 | 独立服务 + Web UI | 命令行工具 | 纯 Shell 脚本 |
| 自动续期 | ✅ 内置定时任务 | ✅ 需配置 cron/systemd | ✅ 内置 cron 自动安装 |
| 多服务器部署 | ✅ 原生支持 | ❌ 需手动复制或配合其他工具 | ❌ 需手动复制或 rsync |
| Web 管理界面 | ✅ 有 | ❌ 无 | ❌ 无 |
| 证书监控/告警 | ✅ 到期提醒、失败告警 | ❌ 无 | ❌ 无 |
| Nginx/Apache 自动配置 | ⚠️ 需手动配置或配合 | ✅ 自动修改配置 | ❌ 手动配置 |
| 安装复杂度 | 中等(需部署服务) | 低(包管理器安装) | 低(curl 一键安装) |
| 依赖 | Java/Node + 数据库 | Python + 依赖包 | 纯 Shell,几乎零依赖 |
| 适合场景 | 企业级、多服务器、集中管理 | 个人/单服务器、快速上手 | 轻量环境、Docker、嵌入式 |
一、 执行步骤
第一步:安全备份(回退保障)
在修改任何配置前,先备份现有的证书文件和 Nginx 配置文件(以编译安装的为例,通过系统包管理工具安装的目录可能不一致!)。如果后续任何环节出错,直接还原即可。
# 备份当前可用的 SSL 证书文件(如果不可用了可以忽略)
cp /usr/local/nginx/conf/ssl/XXX.pem /usr/local/nginx/conf/ssl/XXX.pem.bak
cp /usr/local/nginx/conf/ssl/XXX.key /usr/local/nginx/conf/ssl/XXX.key.bak
# 备份 Nginx 配置文件(主配置)
cp /usr/local/nginx/conf/nginx.conf /usr/local/nginx/conf/nginx.conf.bak
# 如果是单独配置
cp /usr/local/nginx/conf/vhost/XXXX.conf /usr/local/nginx/conf/vhost/XXXX.conf.bak
第二步:安装与配置 acme.sh
# 安装 acme.sh(替换为你的真实邮箱)
curl https://get.acme.sh | sh -s email=your_email@example.com
source ~/.bashrc
# 切换默认 CA 为 Let's Encrypt
acme.sh --set-default-ca --server letsencrypt
# 切换默认 CA 为 ZeroSSL (需要注册登录)
acme.sh --set-default-ca --server zerossl
# 如果之前注册过,填写id和key
acme.sh --register-account --server zerossl \
--eab-kid 你的eab-kid \
--eab-hmac-key 你的eab-hmac-key
# 如果没注册过可以运行以下命令,访问弹出的链接注册
acme.sh --register-account -m 你的邮箱@XXX.com --server zerossl
# 开启 acme.sh 自动升级
acme.sh --upgrade --auto-upgrade
如果是国内的服务器或者主机,请使用gitee
git clone https://gitee.com/neilpang/acme.sh.git
cd acme.sh
./acme.sh --install -m 你的邮箱
第三步:申请证书(5种方式)
1. 对比
| 验证方式 | 核心特点 | 适用场景 | 缺点/限制 |
|---|---|---|---|
--nginx |
智能读取 Nginx 配置,自动完成验证,验证后恢复原状,不修改原配置。 | 服务器上已运行 Nginx 服务。 | 仅支持单域名,无法签发泛域名(通配符)证书。 |
--apache |
与 Nginx 模式类似,智能读取 Apache 配置完成验证。 | 服务器上已运行 Apache 服务。 | 仅支持单域名,无法签发泛域名证书。 |
--dns |
通过 DNS 解析 TXT 记录验证。支持自动 API 和手动两种模式。 | 唯一支持签发泛域名(通配符)证书的方式;适合无公网 IP 或 80 端口受限的服务器。 | 若使用手动模式,每次续期都需手动添加 TXT 记录;需配置 DNS API 凭据才能实现自动续期。 |
--webroot |
在现有的网站根目录下生成验证文件,验证后自动删除。 | 已有 Web 服务,且不想中断服务或修改 Web 配置。 | 无法签发泛域名证书;需确保 Web 服务器对 .well-known 目录有访问权限。 |
--standalone |
脚本自身伪装成 Web 服务器,临时监听 80 或 443 端口完成验证。 | 服务器上没有运行任何 Web 服务,且 80 端口空闲。 | 验证期间会占用端口,若端口已被占用需先停止现有 Web 服务。 |
2. 各方式所需参数与命令示例
1. Nginx / Apache 模式
- 所需参数:
-d(指定域名) - 命令示例:
# Nginx 模式 acme.sh --issue --nginx -d example.com -d www.example.com # Apache 模式 acme.sh --issue --apache -d example.com
2. DNS 验证模式
- 所需参数:
-d(指定域名),--dns(DNS 提供商代码或留空) -
命令示例:
# 手动 DNS 验证(需手动去控制台添加 TXT 记录,不支持自动续期) acme.sh --issue --dns -d example.com --yes-I-know-dns-manual-mode-enough-go-ahead-please # 自动 DNS API 验证(以阿里云为例,支持泛域名和自动续期) export Ali_Key="你的AccessKey" export Ali_Secret="你的SecretKey" acme.sh --issue --dns dns_ali -d example.com -d *.example.com - 注意:环境变量名称不支持自定义!acme.sh 支持超过 100 家 DNS 服务商。如果你使用的是其他服务商,可以通过以下方式查询准确的变量名:查看 acme.sh 官方 Wiki 的 DNS API 列表https://github.com/acmesh-official/acme.sh/wiki/dnsapi
3. Webroot 模式
- 所需参数:
-d(指定域名),-w或--webroot(网站根目录路径) - 命令示例:
acme.sh --issue -d example.com -w /home/wwwroot/example.com/
4. Standalone 独立服务模式
- 所需参数:
-d(指定域名),--standalone - 命令示例:
acme.sh --issue --standalone -d example.com
--dns 并配置对应的 DNS API;如果只是为单个网站开启 HTTPS,且使用的是 Nginx/Apache,使用 --nginx / --apache 或 --webroot 最为简单安全。
第四步:安装证书与平滑重载
将证书复制到 Nginx 目录并绑定重载命令:
acme.sh --install-cert -d XXX.com \
--key-file /usr/local/nginx/conf/ssl/XXX.com.key \
--cert-file /usr/local/nginx/conf/ssl/XXX.com.pem \
--fullchain-file /usr/local/nginx/conf/ssl/fullchain.pem \
--reloadcmd "nginx -t && nginx -s reload"
注意在配置nginx配置文件中的ssl_certificate参数时,推荐使用fullchain.pem,如果是使用域名.pem可能会缺失中间证书,客户端验证时信任链会锻炼导致响应错误!
检查 Nginx 配置语法并平滑重载:
/usr/local/nginx/sbin/nginx -t
/usr/local/nginx/sbin/nginx -s reload
第五步:配置定时任务(自动续期)
acme.sh 在安装时会自动创建 Cron 定时任务,但生产环境必须手动验证其是否生效,防止因系统更新或权限问题导致续期失败。
- 检查现有 Cron 任务
crontab -l | grep acme.sh
正常输出应包含类似以下内容:
0 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null
- 若任务缺失,手动添加
acme.sh --cron --install-cronjob
- 验证续期逻辑(Dry Run)
无需等待证书到期,可强制触发一次续期检查,确认 DNS API 调用及 Nginx Reload 是否正常:
acme.sh --cron --force
注意:
--force会强制触发续期流程,即使证书未到期。生产环境建议仅在首次配置后验证一次,避免频繁触发 Let's Encrypt 的 Rate Limit。
二、 🚨避坑指南
DNS API 的 AccessKey ?
为了生产环境安全,必须在 RAM 访问控制中创建专属子用户(如 acme-bot),仅授予 AliyunDNSFullAccess 权限。这样即使服务器被攻破,黑客也只能操作 DNS,无法动你的 ECS 或数据库。
为什么不能直接用 acme.sh 的默认目录?
acme.sh 默认的证书目录(~/.acme.sh/)是工具内部工作目录。必须通过 --install-cert 命令将证书复制到 Nginx 的固定目录中,并配合 --reloadcmd 参数,这样未来的自动续期才能正确触发 Nginx 重载。
常见错误 1:ERR_SSL_VERSION_OR_CIPHER_MISMATCH
现象:Nginx reload 成功,但浏览器访问提示“您的连接不是私密连接”或 ERR_SSL_VERSION_OR_CIPHER_MISMATCH。
原因分析:旧版 Nginx 配置中包含了 RC4-SHA 等已被现代浏览器彻底废弃的弱加密算法。
解决方案:替换 ssl_ciphers 为现代浏览器支持的强加密套件。
# 错误配置示例
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:...:RC4-SHA:...;
# 正确配置示例
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA;
常见错误 2:ECC 证书验证命令误用
现象:使用 openssl ec -noout -modulus 验证私钥时报错 unknown option -modulus,或者输出的 MD5 值为空字符串的哈希值(d41d8cd98f00b204e9800998ecf8427e)。
原因分析:-modulus 仅适用于 RSA 密钥。acme.sh 默认生成的 ECC 私钥为 PKCS#8 格式,不支持该参数。
解决方案:使用通用的 pkey 命令提取公钥进行比对。
# 验证证书公钥
openssl x509 -noout -pubkey -in /path/to/fullchain.cer | openssl md5
# 验证私钥公钥(注意使用 pkey)
openssl pkey -noout -pubout -in /path/to/private.key | openssl md5
注:两个命令输出的 MD5 值必须完全一致,否则证书与私钥不匹配。
常见错误 3:ERR_CERT_COMMON_NAME_INVALID
现象:访问 XXX.com 时,浏览器提示证书通用名称无效。点击证书发现仅包含 XXX.com。
原因分析:申请证书时仅指定了 -d XXX.com,未包含 www 子域名。现代浏览器要求 SAN(使用者可选名称)必须精确匹配访问的域名。
解决方案:强制重新签发包含所有域名的证书。
acme.sh --force --issue --dns dns_ali -d XXX.com -d XXX.com
注:如果希望一劳永逸,建议直接申请泛域名证书 -d '*.XXX.com'。
常见错误 4:签发时报错 curl error code: 60
现象:执行 acme.sh --issue 时,提示 Cannot init API for: https://acme-v02.api.letsencrypt.org/directory,且伴随 CURLE_PEER_FAILED_VERIFICATION (60) 错误。
原因分析:服务器的 CA 证书包过期或缺失,导致本地 curl 无法验证 Let's Encrypt 官方服务器的 SSL 证书信任链。
解决方案:更新系统 CA 证书包。
# CentOS / Alibaba Cloud Linux
yum update -y ca-certificates
# Ubuntu / Debian
apt-get update && apt-get install --reinstall ca-certificates
更新后,使用 curl -v https://acme-v02.api.letsencrypt.org/directory 验证能正常返回 JSON 数据即可重新签发。
三、 任务总结
- 安全性提升:全程通过备份机制保障生产环境可回退;使用 RAM 子用户保护云账号安全。
- 运维效率提升:彻底告别手动续签,
acme.sh的 crontab 任务会在证书到期前 30 天自动续期并平滑重载 Nginx,实现真正的“一次配置,永久免维护”。 - 可观测性增强:通过第五步的 Dry Run 验证,确保自动续期链路在首次配置时即通过验证,避免“证书到期才发现 Cron 失效”的生产事故。