ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

acme.sh + 阿里云DNS + Nginx:自动化SSL证书管理实战指南

2026/8/17 7:42:52 拓冰建站 浏览量
acme.sh + 阿里云DNS + Nginx:自动化SSL证书管理实战指南 1. 项目缘起为什么我放弃了付费证书和手动申请做Web服务给网站套上HTTPS的锁头现在已经是基本操作了。早些年我还会去各大云服务商那里买付费的泛域名证书一年大几百甚至上千图个省心。后来Let‘s Encrypt横空出世免费SSL证书成了标配但每次申请、部署、续期那一套certbot命令敲下来再手动改Nginx配置还是觉得麻烦尤其是域名多的时候。直到我遇到了acme.sh。这工具彻底改变了我的证书管理方式。它把申请、验证、部署、续期这一整套流程全自动化了而且是纯Shell脚本写的轻量到几乎可以在任何Linux环境跑起来。最让我心动的是它对泛域名通配符证书*.example.com的支持配合阿里云DNS的API可以实现完全无人值守的自动化。所以今天这篇内容就是把我这几年在Debian服务器上用acme.sh 阿里云DNS Nginx这套组合拳的实战经验毫无保留地拆解给你看。这不是一个简单的命令罗列我会带你走一遍完整的流程并重点分享那些官方文档里不会写但实际部署中一定会遇到的“坑”和应对技巧。无论你是管理着十几个服务的运维还是只有一个个人博客的开发者这套方案都能让你从此和证书过期告警说再见。2. 核心工具链选型为什么是acme.sh Aliyun DNS Nginx在开始动手之前我们得先搞清楚为什么选这三样东西组合在一起。市面上类似的方案不少比如certbot官方工具或者其它ACME客户端。我的选择是基于下面几个实实在在的考量2.1 为什么选择acme.sh而非certbotcertbot是EFF电子前沿基金会的亲儿子知名度最高。但它有几个点让我最终转向了acme.sh零依赖与轻量acme.sh就是一个Shell脚本安装后除了系统自带的curl、cron等几乎不引入额外依赖。而certbot通常需要Python环境和一堆包在最小化安装的系统上可能更折腾。配置的持久化与独立性acme.sh会把你的账户信息、API密钥等配置以及生成的证书都放在用户主目录下的.acme.sh/文件夹里。这个设计非常“Unix哲学”所有东西都在一个地方备份和迁移极其简单。certbot的配置则分散在/etc/letsencrypt/等系统目录权限管理上有时会更复杂。更灵活的DNS API支持对于泛域名证书DNS验证是唯一的方式因为HTTP文件验证无法验证*.example.com这样的通配符。acme.sh原生支持超过50种DNS服务商的API包括国内最常用的阿里云、腾讯云、Cloudflare等而且集成方式非常统一。虽然certbot也可以通过插件实现但acme.sh的集成感觉更原生、更稳定。自动续期与通知acme.sh在安装时会自动创建一个cron任务或systemd timer每天检查证书是否快过期默认是到期前30天并自动续期。续期成功后它还能通过配置的钩子脚本比如reloadcmd自动重启Nginx真正实现“一次配置永久有效”。这个自动化闭环做得非常彻底。2.2 为什么选择阿里云DNS进行验证对于在中国大陆有业务的网站域名解析服务很多都放在阿里云万网或腾讯云。选择阿里云DNS API进行验证原因很直接延迟与可靠性DNS验证需要向你的域名DNS服务商添加一条特定的TXT记录。使用阿里云自己的API在阿里云服务器上操作解析和生效速度最快避免了跨网络、跨服务商可能带来的超时或失败。API成熟稳定阿里云的DNS API文档齐全功能稳定访问控制策略RAM配置清晰安全性有保障。无缝集成acme.sh对阿里云DNS的支持是“一等公民”级别的只需要提供两个密钥剩下的验证、添加记录、删除记录全自动完成你完全不用关心DNS记录是怎么被操作的。2.3 为什么是Nginx这几乎是事实标准了。Nginx作为Web服务器和反向代理的普及率极高性能优秀配置相对清晰。acme.sh对Nginx的支持也非常好不仅可以自动安装证书还能在续期后自动重载配置确保服务不间断。我们的操作将主要围绕Nginx的配置文件展开。3. 前期准备阿里云RAM访问控制配置详解这是整个流程中最关键也最容易出错的一步。我们不能直接使用主账户的AccessKey那相当于把家门钥匙扔在外面。必须创建一个最小权限的子用户RAM用户只赋予它管理指定域名DNS记录的权限。3.1 创建RAM用户并获取AccessKey登录阿里云控制台进入访问控制RAM页面。在左侧菜单选择用户点击创建用户。输入一个用户名例如acme-dns-operator。“控制台访问”务必选择“禁用”因为这个用户只需要API调用权限不需要登录控制台。点击“确定”。创建成功后系统会显示AccessKey ID和AccessKey Secret。这是唯一一次完整显示Secret的机会请务必立即保存到安全的地方如本地密码管理器。如果丢失只能创建新的AccessKey。3.2 为RAM用户授权精细化的DNS策略接下来我们需要创建一个自定义策略精确控制这个RAM用户只能操作我们指定域名的DNS记录。在RAM控制台进入权限管理 权限策略点击创建权限策略。选择脚本编辑模式这样权限最清晰。在策略文档中填入以下内容。这里以你的域名是yourdomain.com为例{ Version: 1, Statement: [ { Effect: Allow, Action: [ alidns:DescribeDomains, alidns:DescribeDomainRecords, alidns:AddDomainRecord, alidns:DeleteDomainRecord, alidns:UpdateDomainRecord ], Resource: acs:alidns:*:*:domain/yourdomain.com }, { Effect: Allow, Action: [ alidns:DescribeDomains ], Resource: * } ] }让我解释一下这个策略的每一部分第一个Statement核心权限。允许对资源acs:alidns:*:*:domain/yourdomain.com即你的具体域名执行一系列DNS记录操作。包括查看域名和记录、添加、删除、更新记录。这正好覆盖了acme.sh验证所需的所有动作添加_acme-challengeTXT记录并在验证完成后删除它。第二个Statement辅助权限。允许执行DescribeDomains动作且资源为*所有域名。这是必须的因为acme.sh在初始阶段需要调用这个API来获取你的域名列表确认域名存在于你的账户下。但这个权限仅限于“查看”不能修改其他域名。注意权限粒度的把握。网上有些教程会直接授予alidns:*所有DNS权限或者*所有权限这是极其危险的做法。遵循最小权限原则能有效避免因密钥泄露或脚本异常导致整个云账户DNS被清空等灾难性后果。给这个策略起个名字例如AcmeDNSManagePolicy然后创建。回到用户页面找到你创建的acme-dns-operator用户点击添加权限。在“授权范围”选择整个云账号在权限策略名称中搜索并选中刚刚创建的AcmeDNSManagePolicy完成授权。至此阿里云端的准备工作就完成了。你手头应该有了AccessKey IDAccessKey Secret一个仅能管理yourdomain.com域名DNS记录的RAM用户4. 服务器端部署安装acme.sh与申请首张泛域名证书现在我们登录到你的Debian服务器。假设你使用的是非root用户例如debian但拥有sudo权限。4.1 安装acme.sh安装过程简单到令人发指。官方推荐的方式是直接通过curl下载脚本并安装。它会自动安装到你的用户主目录下并为你创建cron任务。# 切换到你的用户主目录或者任意你有写权限的目录 cd ~ # 下载安装脚本并执行。注意这里不需要sudo它以当前用户身份安装。 curl https://get.acme.sh | sh -s emailyour-emailexample.com安装命令中的your-emailexample.com非常重要这是你在Let‘s Encrypt的注册邮箱用于接收证书过期提醒等通知虽然基本用不上因为会自动续期但这是ACME协议的要求。安装完成后脚本会自动为你做以下几件事在~/.acme.sh/目录下克隆完整的项目。创建一个别名acme.sh方便你调用。但需要重新加载一下Shell配置才能生效。自动添加一个每日执行的cron任务或systemd timer用于检查并续期证书。为了让acme.sh命令立即可用执行source ~/.bashrc # 或者如果你用的是zsh: source ~/.zshrc现在运行acme.sh --version应该能正确显示版本信息。4.2 配置阿里云DNS API密钥我们需要把上一步获取的AccessKey告诉acme.sh。它提供了多种方式最安全方便的是设置成环境变量。我们将它们设置为当前Shell会话的环境变量这样在接下来的命令中就能直接使用。export Ali_Key你的AccessKey ID export Ali_Secret你的AccessKey Secret重要提示这种方式设置的变量只在当前终端会话有效。为了避免每次申请证书都要重新输入更常见的做法是将这两个变量写入你的Shell配置文件如~/.bashrc或~/.profile的末尾。但请注意这会让你密钥以明文形式存储在文件中。你可以权衡便利性与安全性。对于个人服务器通常可以接受对于生产环境可以考虑使用其他更安全的密钥管理方式或者每次手动导出。4.3 签发你的第一张泛域名SSL证书激动人心的时刻到了。我们将使用DNS验证方式申请一张覆盖*.yourdomain.com和yourdomain.com根域名的证书。acme.sh --issue --dns dns_ali -d *.yourdomain.com -d yourdomain.com逐参数解释--issue发起证书签发请求。--dns dns_ali指定使用DNS验证方式并且是阿里云的DNSdns_ali是acme.sh内置的阿里云插件标识。-d *.yourdomain.com指定通配符域名。-d yourdomain.com同时指定根域名。这样一张证书就同时保护了所有子域名和主域名。执行这个命令后acme.sh会开始它的魔法它会利用你提供的Ali_Key和Ali_Secret调用阿里云DNS API在你的域名yourdomain.com下自动添加一条名为_acme-challenge.yourdomain.com的TXT记录记录值是一串随机字符串。然后它会等待一段时间通常几十秒并反复查询DNS直到确认这条TXT记录在全球DNS中生效。DNS验证通过后它会向Let‘s Encrypt的服务器发送验证成功的信号。Let‘s Encrypt确认后就会签发证书。acme.sh会将证书文件下载并保存到~/.acme.sh/*.yourdomain.com/目录下。最关键的一步脚本会自动调用阿里云API删除刚才添加的那条临时的TXT记录。整个过程中你完全不需要手动去操作DNS控制台。如果一切顺利你会看到绿色的 “Cert success.” 字样恭喜你证书已经签发成功了证书文件就躺在~/.acme.sh/*.yourdomain.com/目录里。主要的文件是fullchain.cer证书链文件你的证书中间CA证书Nginx配置中ssl_certificate指令需要的就是它。*.yourdomain.com.key私钥文件Nginx配置中ssl_certificate_key指令需要它。5. 证书部署与Nginx配置自动化证书生成了但还在用户目录下。我们需要把它放到一个Nginx能访问的标准位置比如/etc/ssl/并配置Nginx使用它。acme.sh的--install-cert命令可以一键完成这个操作。5.1 创建标准的证书存储目录首先我们创建一个目录来存放证书。通常系统级的SSL证书放在/etc/ssl/或/etc/nginx/ssl/下。这里我习惯用后者因为更清晰。# 使用sudo创建目录因为Nginx进程需要能读取这些文件 sudo mkdir -p /etc/nginx/ssl/yourdomain.com5.2 使用acme.sh自动安装证书到指定位置现在运行安装命令。这个命令不仅会复制证书文件还会为你设置一个自动续期后的钩子。acme.sh --install-cert -d *.yourdomain.com \ --key-file /etc/nginx/ssl/yourdomain.com/privkey.pem \ --fullchain-file /etc/nginx/ssl/yourdomain.com/fullchain.pem \ --reloadcmd sudo systemctl reload nginx参数详解--install-cert -d “*.yourdomain.com”指定要为哪个域名安装证书。--key-file指定私钥文件的目标路径和名称。这里我命名为privkey.pem清晰易懂。--fullchain-file指定证书链文件的目标路径和名称。命名为fullchain.pem。--reloadcmd这是精华所在指定一个在证书成功续期后要执行的命令。我们这里填的是sudo systemctl reload nginx。reload是平滑重载配置不会中断当前连接是更新证书的最佳方式。执行这个命令后acme.sh会将~/.acme.sh/*.yourdomain.com/下的私钥和证书链文件复制到你指定的/etc/nginx/ssl/yourdomain.com/目录。在它的配置里记录下这个“安装”信息。以后当自动续期触发并成功获取新证书后它会自动将新证书复制到同样的位置然后执行你定义的reloadcmd命令重启Nginx。至此全自动化闭环完成。5.3 配置Nginx使用新证书现在我们需要修改Nginx的站点配置文件指向我们新的证书文件。假设你的站点配置文件是/etc/nginx/sites-available/yourdomain.com。打开该文件找到或添加监听443端口的server块关键配置如下server { listen 443 ssl http2; # 启用http2是个好习惯 listen [::]:443 ssl http2; server_name yourdomain.com www.yourdomain.com; # 指向acme.sh安装的证书 ssl_certificate /etc/nginx/ssl/yourdomain.com/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/yourdomain.com/privkey.pem; # 下面是一些推荐的SSL优化配置 ssl_protocols TLSv1.2 TLSv1.3; # 禁用不安全的旧协议 ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:...; # 使用安全的加密套件 ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d; # ... 你的其他配置如root目录、proxy_pass等 ... } # 别忘了配置HTTP到HTTPS的重定向 server { listen 80; listen [::]:80; server_name yourdomain.com www.yourdomain.com; return 301 https://$server_name$request_uri; }配置完成后检查Nginx配置语法是否正确sudo nginx -t如果显示syntax is ok和test is successful就可以重载Nginx了sudo systemctl reload nginx现在用浏览器访问https://yourdomain.com你应该能看到绿色的安全锁标志了。子域名https://api.yourdomain.com或https://blog.yourdomain.com等也同样受到保护。6. 进阶配置与深度踩坑指南基础流程走通了但要想在生产环境用得踏实下面这些进阶知识和踩过的坑你必须了解。6.1 证书的存储结构与更新机制理解acme.sh如何管理证书非常重要。在~/.acme.sh/目录下每个域名都有一个独立的文件夹如*.yourdomain.com。这个文件夹里存放着从CA获取的原始证书文件.cer,.key,.csr等。一个名为*.yourdomain.com.conf的配置文件。这个文件是核心它记录了该域名的所有配置使用的验证方式dns_ali、API密钥已混淆、证书安装路径Le_KeyPath,Le_CertPath以及最重要的Le_ReloadCmd就是--reloadcmd指定的命令。当你运行acme.sh --install-cert时它主要就是在更新这个配置文件并执行一次文件复制。而每日的cron任务会遍历所有这样的配置检查证书有效期如果需要续期就重新走一遍签发流程然后根据配置里的Le_ReloadCmd执行重载命令。6.2 如何为多个域名或泛域名配置如果你有多个主域名例如domain-a.com和domain-b.com只需要对每个域名重复第4章和第5章的过程即可。acme.sh会为每个域名创建独立的配置和cron任务。如果你有一个域名下有多个不同的泛域名需求虽然不常见例如*.app.domain.com和*.api.domain.com你需要为每个不同的通配符前缀单独申请证书因为*.domain.com无法覆盖二级子域名。命令类似acme.sh --issue --dns dns_ali -d “*.app.yourdomain.com” acme.sh --issue --dns dns_ali -d “*.api.yourdomain.com”6.3 证书续期与状态检查自动化续期是acme.sh的最大亮点但我们也需要知道如何手动检查和干预。手动续期acme.sh --renew -d “*.yourdomain.com” --force。--force参数表示即使证书未到期也强制更新。通常在测试续期流程或紧急更换密钥时使用。查看证书信息acme.sh --info -d “*.yourdomain.com”。这会显示证书的路径、到期时间、以及配置的reloadcmd等信息。查看所有证书acme.sh --list。列出当前账户下所有由acme.sh管理的证书。6.4 我踩过的那些“坑”与解决方案坑cron任务不执行或执行失败现象证书快过期了却没自动续期。排查首先检查cron日志sudo grep CRON /var/log/syslog看是否有acme.sh的任务记录。更直接的方法是查看acme.sh自己的日志~/.acme.sh/acme.sh.log。常见原因与解决环境变量丢失cron执行的环境与用户Shell环境不同如果你在.bashrc中设置了Ali_Key和Ali_Secretcron是读不到的。最可靠的方案是在使用acme.sh --issue命令时直接通过--dns选项的扩展参数传递密钥或者使用acme.sh的--config-home功能将配置存到特定位置。但更简单的方法是在~/.acme.sh/account.conf文件中直接写入SAVED_Ali_Key‘xxx‘和SAVED_Ali_Secret‘xxx‘。acme.sh会自动读取这个文件中的变量。sudo权限问题你的--reloadcmd是sudo systemctl reload nginx。cron任务默认以当前用户运行执行sudo时需要密码。解决方法是为该用户配置Nginx服务的免密码sudo。执行sudo visudo在文件末尾添加一行debian ALL(ALL) NOPASSWD: /bin/systemctl reload nginx。这样debian用户就可以无需密码执行reload nginx了。坑DNS验证超时现象执行--issue命令时卡在 “Pending, The CA is processing your order, please just wait.” 或 “Verify error:Invalid response from” 然后失败。排查这通常是DNS记录没有及时在全球生效DNS传播延迟。阿里云添加记录通常很快但某些地区DNS缓存可能导致延迟。解决增加等待时间acme.sh默认等待120秒。你可以通过--dnssleep参数增加等待时间例如--dnssleep 300等待5分钟。使用备用DNS在命令后添加--server letsencrypt可以切换到Let‘s Encrypt的主服务器有时有奇效。或者使用--server buypass或--server zerossl等其他ACME CA可能需要额外注册。手动验证在命令执行后立刻去阿里云DNS控制台查看是否已经生成了_acme-challenge的TXT记录。然后用dig TXT _acme-challenge.yourdomain.com 8.8.8.8命令查询谷歌DNS看是否已解析。如果控制台有但公共DNS查不到就耐心等一会儿。坑续期后Nginx未重载服务仍使用旧证书现象查看acme.sh日志发现续期成功但浏览器访问网站提示证书过期。排查检查~/.acme.sh/*.yourdomain.com/*.conf配置文件中的Le_ReloadCmd是否正确。检查/etc/nginx/ssl/yourdomain.com/下的证书文件修改时间是否已更新。解决很可能reloadcmd命令执行失败了。可以手动运行一下配置中的命令看是否有报错。确保Nginx的配置文件语法始终正确nginx -t因为如果配置有误systemctl reload nginx会静默失败。一个更健壮的做法是将reloadcmd写成一个脚本在脚本里先检查语法再重载并记录日志# 创建脚本 /usr/local/bin/reload-nginx.sh #!/bin/bash if sudo nginx -t /dev/null 21; then sudo systemctl reload nginx echo “[$(date)] Nginx reloaded for new cert.” /var/log/acme-renew.log else echo “[$(date)] Nginx config test failed! Cannot reload.” /var/log/acme-renew.log # 可以在这里添加发送报警邮件的逻辑 fi然后给脚本执行权限并在--install-cert时指定--reloadcmd “/usr/local/bin/reload-nginx.sh”。7. 生产环境维护与监控建议当你的网站跑起来后证书管理就变成了一个后台静默工作。为了确保万无一失我建议你建立简单的监控。7.1 证书过期监控虽然acme.sh会自动续期但监控是最后一道防线。你可以用简单的Shell脚本定期检查证书过期时间并发送通知。#!/bin/bash # check_cert_expiry.sh DOMAIN“*.yourdomain.com” CERT_FILE“/etc/nginx/ssl/yourdomain.com/fullchain.pem” # 获取证书过期时间Unix时间戳 EXPIRY_DATE$(openssl x509 -enddate -noout -in “$CERT_FILE” | cut -d -f2) EXPIRY_TIMESTAMP$(date -d “$EXPIRY_DATE” %s) # 获取当前时间戳 CURRENT_TIMESTAMP$(date %s) # 计算剩余天数 DAYS_LEFT$(( (EXPIRY_TIMESTAMP - CURRENT_TIMESTAMP) / 86400 )) # 设置告警阈值例如小于10天 WARNING_DAYS10 if [ “$DAYS_LEFT” -lt “$WARNING_DAYS” ]; then # 发送告警这里可以用邮件、Slack、钉钉机器人等 echo “警告: 证书 ${DOMAIN} 将在 ${DAYS_LEFT} 天后过期” | \ mail -s “SSL证书过期警告” your-emailexample.com # 或者调用curl发送到你的监控平台 # curl -X POST -H ‘Content-Type: application/json‘ -d “{\“text\“:\“证书 ${DOMAIN} 即将过期\“}” YOUR_WEBHOOK_URL fi将这个脚本加入cron每周运行一次即可。7.2 密钥与证书文件的权限安全/etc/nginx/ssl/目录下的私钥文件.key或.pem是最高机密。务必确保其权限设置正确sudo chown root:root /etc/nginx/ssl/yourdomain.com/ sudo chmod 700 /etc/nginx/ssl/yourdomain.com/ # 目录仅root可读写执行 sudo chown root:root /etc/nginx/ssl/yourdomain.com/privkey.pem sudo chmod 600 /etc/nginx/ssl/yourdomain.com/privkey.pem # 私钥仅root可读证书文件.pem或.cer可以放宽一点权限因为里面是公开信息但通常也设置为644root可读写其他人只读。7.3 定期回顾与升级升级acme.shacme.sh本身会定期自动升级。你也可以手动运行acme.sh --upgrade。新版本可能会修复bug或增加对新DNS API的支持。回顾RAM权限每年检查一次阿里云RAM用户的AccessKey如果长期未使用考虑轮换。检查RAM策略确保权限依然是最小化的。测试续期流程在证书到期前几个月可以手动运行一次acme.sh --renew -d “*.yourdomain.com” --force来测试整个续期、安装、重载流程是否依然顺畅。