为网站启用HTTPS已经成为基本安全实践,而Let’s Encrypt提供的免费证书配合自动化客户端极大降低了部署门槛。常见的HTTP-01验证方式要求ACME服务端能够通过80端口访问到服务器上的验证文件,但在不少场景下这个前提并不成立:服务器可能只开放了443端口,前端挂着CDN或负载均衡,亦或是整个服务运行在完全没有公网IP的私有网络里。此时DNS-01验证成为唯一可行的路线。acme.sh作为一款轻量、支持多种DNS服务商的ACME客户端,允许通过添加TXT记录的方式来证明域名所有权,完全绕开HTTP端口依赖,同时还支持签发通配符证书。

acme.sh与DNS验证机制
acme.sh是一个用Shell脚本编写的ACME协议客户端,支持Let’s Encrypt、ZeroSSL等证书颁发机构。它的核心优势在于对DNS验证的内置支持:用户只需要提供DNS服务商的API凭据,acme.sh就可以自动创建用于验证的TXT记录,待CA完成校验后再自动删除该记录。整个过程无需手动干预,也无需在服务器上临时开放任何端口或放置文件。
DNS验证相比HTTP验证,不仅解决了端口限制问题,还能申请通配符证书(例如*.ippipp.com),一张证书即可覆盖主域和所有二级子域。验证流程大致如下:acme.sh调用DNS服务商API,在域名的_acme-challenge主机名下创建一条TXT记录,内容为ACME服务端下发的随机token;CA向权威DNS服务器查询该记录,匹配成功后签发证书;acme.sh接着删除临时记录,完成清理。因此,只要使用的DNS服务商提供操作API,且acme.sh已适配该服务商的接口,整个流程就能全自动运行。
目前acme.sh已经原生支持超过上百家DNS服务商,包括阿里云、腾讯云、Cloudflare、DNSPod、GoDaddy等主流选择。针对尚未内置适配的服务商,acme.sh也提供了通用的手动DNS模式或自定义API脚本,灵活性很高。
安装acme.sh并配置DNS API
安装acme.sh只需要一条命令,推荐使用非root用户执行,脚本会自动将程序安装在当前用户的~/.acme.sh/目录下,并添加定时任务用于证书续期。以普通用户身份运行:
curl https://get.acme.sh | sh # 或者使用git克隆方式 git clone https://github.com/acmesh-official/acme.sh.git cd acme.sh ./acme.sh --install
安装完成后需要让Shell重新加载配置,或者执行source ~/.bashrc即可使用acme.sh命令。接下来最关键的一步是配置DNS服务商的API凭据。以阿里云DNS为例,首先需要在RAM访问控制中创建一个具有管理云解析(AliyunDNSFullAccess)权限的子账号,获取AccessKey ID和AccessKey Secret。然后在Shell中导出环境变量:
export Ali_Key="你的AccessKey ID" export Ali_Secret="你的AccessKey Secret"
为了保护凭据安全,acme.sh支持将这些变量保存在独立的账号配置文件中,避免泄露到Shell历史记录。执行acme.sh --save --dns dns_ali会将凭据保存至~/.acme.sh/account.conf,后续签发证书时会自动读取。不同服务商的环境变量名称和前缀略有差异,具体可以参考acme.sh官方文档的dnsapi部分。
签发通配符证书并集成Nginx
配置好API凭据后,签发证书非常简单。假设域名为ippipp.com,想要申请一张同时覆盖主域和所有子域的通配符证书,可使用以下命令:
acme.sh --issue --dns dns_ali -d ippipp.com -d '*.ippipp.com'
参数--dns dns_ali指定使用阿里云DNS验证,证书申请成功后默认存储在~/.acme.sh/ippipp.com/目录下,包含完整证书链、私钥等文件。不过该目录下的文件仅供acme.sh内部使用,不要直接让Nginx引用此路径,因为日后续期时路径结构可能会变化。正确的做法是执行安装命令将证书复制到Nginx约定的目录:
acme.sh --install-cert -d ippipp.com --key-file /etc/nginx/ssl/ippipp.com.key --fullchain-file /etc/nginx/ssl/ippipp.com.cer --reloadcmd "systemctl reload nginx"
上面这条命令会将私钥复制到/etc/nginx/ssl/ippipp.com.key,完整证书链复制到/etc/nginx/ssl/ippipp.com.cer,并且每次成功续期后自动执行systemctl reload nginx使新证书生效。Nginx配置中只需要指向这两个文件即可:
server {
listen 443 ssl http2;
server_name ippipp.com *.ippipp.com;
ssl_certificate /etc/nginx/ssl/ippipp.com.cer;
ssl_certificate_key /etc/nginx/ssl/ippipp.com.key;
# 其他SSL协议与加密套件配置省略
# ...
}
重载Nginx后,通过浏览器访问即可看到由Let’s Encrypt签发的有效证书。对于泛域名场景,所有二级子域(如www、api、admin)都可共用同一张证书,大幅简化了管理复杂度。如果服务器内部署了多个虚拟主机,也可以为不同域名分别申请证书,只需分别执行--install-cert并指定不同的-d列表和文件路径。
自动续期与定时任务管理
acme.sh安装时已经自动创建了crontab任务,每天会检查证书有效期,对于60天内到期的证书自动执行续期。用户无需额外配置定时任务,除非需要调整检查频率或自定义续期策略。默认任务大致如下:
10 0 * * * "/home/user/.acme.sh"/acme.sh --cron --home "/home/user/.acme.sh" > /dev/null
续期过程会复用签发时使用的DNS API凭据和参数,因此只要API密钥不过期,整个过程完全是静默的。如果担心API凭据泄露或变更,可以定期更新保存在account.conf中的变量,同时确保RAM账号权限最小化。续期成功后,--reloadcmd指定的命令会被触发,Nginx无缝加载新证书,客户端请求不会出现证书过期错误。
如果想手动触发一次续期测试,可以使用acme.sh --renew -d ippipp.com --force,强制忽略有效期重新签发。在更换DNS服务商或迁移服务器后,往往需要重新执行--install-cert来更新证书存放路径和重载命令,但已有的续期配置会保留。
常见问题与排查思路
DNS验证失败:TXT记录未生效
acme.sh添加TXT记录后,CA会立即查询DNS。如果记录尚未传播到权威服务器,就会校验失败。部分DNS服务商API写入速度较快,但仍可能出现延迟。acme.sh默认等待120秒,可以通过--dnssleep参数延长等待时间。如果始终失败,需检查API凭据是否正确、是否有权限添加_acme-challenge记录,或是否存在CNAME冲突。
Nginx重载后证书未更新
有时虽然文件已经替换,但Nginx worker进程没有加载新证书。确认--reloadcmd中的命令能正常执行,且/etc/nginx/ssl/目录权限正确。也可以尝试改用systemctl restart nginx,但重启会短暂中断现有连接,建议先用nginx -t测试配置再执行重载。
泛域名证书匹配二级子域问题
通配符证书仅匹配同一级别的子域,例如*.ippipp.com可以覆盖www.ippipp.com,但无法匹配sub.www.ippipp.com。如果需要覆盖多级子域,必须为每个层级单独申请证书,或者在应用层合并为二级子域进行部署。
API凭据安全
AccessKey Secret务必妥善保管,不要硬编码在脚本中或加入版本控制。acme.sh将凭据保存在权限为600的文件内,相对安全。如果服务器对外开放,建议使用RAM角色或STS临时凭证,降低泄露风险。
整套方案维护成本极低,一次配置即可长期受益。当服务器环境无法满足HTTP验证时,acme.sh的DNS验证方式提供了强大且灵活的替代方案,与Nginx的深度集成更让证书生命周期管理走向全自动。