一、为什么用 acme.sh 而不是 certbot
给网站配 HTTPS 的第一步是拿到可信证书。Let's Encrypt 提供免费证书,传统方案 certbot 需要以 root 身份安装、与系统 Python 环境耦合,且在多域名、DNS 验证(泛域名)、自动续期上配置繁琐。acme.sh 是纯 Shell 实现的 ACME 客户端:一条命令安装、支持 80 端口验证与 DNS API 验证(可签泛域名)、自动续期、自动部署到 Nginx/Apache,还自带脚本升级,是目前 VPS 上最顺手的证书工具。
本文用 acme.sh 走完"安装 → 签发 → 部署 → 自动续期 → 排错"全流程。
二、一条命令安装 acme.sh
用官方安装脚本,curl 管道安装即可(安装到 ~/.acme.sh):
curl https://get.acme.sh | sh -s email=you@example.com
# 安装完成会自动写入 crontab 每日检查续期,确认一下
crontab -l | grep acme
# 查看版本
~/.acme.sh/acme.sh --version
说明:
- 安装时指定邮箱用于证书到期提醒与 CA 联系,务必真实可收信。
- 安装脚本会自动在
~/.bashrc写入 alias,重开终端即可直接使用acme.sh。 - 若服务器在中国大陆访问 Let's Encrypt 慢,可改用 ZeroSSL 或更换 ACME 服务器:
acme.sh --set-default-ca --server letsencrypt(默认是 ZeroSSL,可切回)。
三、Webroot 方式签发单域名证书(80 端口验证)
适用于网站已跑在 80 端口、站点根目录可写的场景,无需停止服务:
# 先确保 80 端口放行,且验证目录可被公网访问
mkdir -p /var/www/example.com/.well-known
~/.acme.sh/acme.sh --issue -d example.com -d www.example.com \
--webroot /var/www/example.com
# 成功后会提示证书路径,类似:
# Your cert is in: /root/.acme.sh/example.com_ecc/example.com.cer
常见坑:.well-known 路径被 Nginx 反代拦截、或 vhost 里没有把验证请求落到对应目录,会导致验证失败(见第六节排错)。
四、DNS API 签发泛域名证书(推荐)
要签 *.example.com 泛域名,必须用 DNS 验证。acme.sh 内置了阿里云、DNSPod、Cloudflare、Namecheap 等数十家 DNS 厂商的 API 支持。以阿里云为例:
# 1. 在阿里云控制台创建 AccessKey,权限只需 DNS 解析管理
export Ali_Key="你的AccessKey ID"
export Ali_Secret="你的AccessKey Secret"
# 2. 签发泛域名(含根域)
~/.acme.sh/acme.sh --issue --dns dns_ali \
-d example.com -d '*.example.com'
# 3. 签发后密钥会保存在 ~/.acme.sh 中,供续期自动复用
# 注意:第一次签完 Ali_Key/Ali_Secret 会被写入配置,之后续期无需再手动导出
其他厂商对应参数:DNSPod 用 --dns dns_dp + DP_Id/DP_Key,Cloudflare 用 --dns dns_cf + CF_Token/CF_Account_ID。DNS 厂商列表可在 acme.sh 官方 DNS API 文档 查询。
签发时若提示"域名验证等待",是因为 acme.sh 需要等待 DNS 生效(TTL 较长时可达 2~3 分钟),耐心等待即可。
五、部署到 Nginx 与自动续期
签发成功后把证书安装到 Nginx 的配置目录,acme.sh 支持 --install-cert 自动拷贝并重载:
~/.acme.sh/acme.sh --install-cert -d example.com \
--key-file /etc/nginx/ssl/example.com.key \
--fullchain-file /etc/nginx/ssl/example.com.pem \
--reloadcmd "systemctl reload nginx"
Nginx 站点配置里引用证书:
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/nginx/ssl/example.com.pem;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
location / {
root /var/www/example.com;
index index.html;
}
}
# HTTP 强制跳转 HTTPS
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
续期:acme.sh 安装时已写入 crontab,每天自动检查,距到期不足 30 天才重新签发,签完自动调用 --reloadcmd 重载 Nginx,全程无人值守。可手动验证一次:
# 手动触发全部证书检查/续期
~/.acme.sh/acme.sh --renew-all
# 查看证书到期时间
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates
六、证书升级、回滚与多域名管理
# 升级 acme.sh 本身
~/.acme.sh/acme.sh --upgrade
# 查看已签发的所有证书
~/.acme.sh/acme.sh --list
# 删除某域名证书(不影响已在用的文件副本)
~/.acme.sh/acme.sh --remove -d example.com
# 强制重新签发(比如换了 DNS 服务商)
~/.acme.sh/acme.sh --issue --dns dns_ali -d example.com -d '*.example.com' --force
多域名同时管理就是多次 --issue/--install-cert,互不干扰。回滚很简单:证书文件都在 ~/.acme.sh/域名_ecc/ 目录,手动从该目录拷回 Nginx 配置路径并 reload 即可。
七、常见错误与排错
1. 验证失败 Can not find domain / Timeout
先确认域名解析指向本机:dig +short example.com;再确认 80 端口对外可达:curl -I http://example.com/.well-known/acme-challenge/test。Nginx 有 301 跳转或防盗链会拦截验证请求,给验证路径放行:
location ^~ /.well-known/acme-challenge/ {
allow all;
root /var/www/example.com;
}
2. 报错 Rate limit exceeded
Let's Encrypt 对同一域名有签发频率限制(每周 5 次/域)。大多是反复测试造成,等一周或换 ZeroSSL 即可;正式环境少用 --force 重签。
3. DNS 验证一直 pending
检查 DNS 服务商的解析是否在 _acme-challenge 记录上正确添加、TTL 是否过长,使用 dig _acme-challenge.example.com TXT 确认生效后再重试。
4. 证书部署后浏览器报错
多为只配了 cert 没配 fullchain(中间链缺失),或服务器时间偏差。用 openssl s_client -connect example.com:443 -servername example.com 检查证书链,并用 chronyc sources 校准时间。
5. 续期失败没有告警
acme.sh 支持 Telegram/钉钉等通知:export TELEGRAM_BOT_TOKEN=... && export TELEGRAM_CHAT_ID=...,或在 crontab 的续期行后追加邮件告警脚本,避免证书悄悄过期。
HTTPS 不只是"配个证书",配合之前的全站跳转与 HSTS、OCSP Stapling(ssl_stapling on),才构成完整的安全链路。



