acme.sh是纯shell实现的轻量级acme客户端,零依赖、安装即用,支持dns/http/standalone验证,内置cron自动续期与nginx重载逻辑,比certbot更稳定适配老旧系统。

直接用 acme.sh 配合 crontab 就能搞定,不用写复杂脚本,也不依赖 Python 环境或 systemd timer —— 这是目前最轻量、最稳定、适配性最强的方案。
为什么选 acme.sh 而不是 certbot
acme.sh 是纯 Shell 实现,无外部依赖,安装即用;certbot 依赖 Python 和大量包,在老旧系统(如 CentOS 7)上常因 OpenSSL 版本或 pip 源问题失败。更重要的是:acme.sh 的续期命令自带重载逻辑,而 certbot 必须额外写 hook 脚本才能 reload Nginx,稍有疏漏就导致证书更新了但服务没生效。
- 证书路径默认在
~/.acme.sh/,不污染系统目录 - 支持
--webroot(推荐)、--standalone、DNS API(阿里云/Cloudflare 等)三种验证方式,无需开放 443 或停服务 - 续期后自动触发
--reloadcmd,只要一条命令就能完成“签发 + 复制 + 重载”全流程
关键命令:一次签发 + 自动部署
假设你的域名是 example.com,网站根目录为 /var/www/html,Nginx 配置中证书路径设为 /etc/nginx/ssl/example.com.pem 和 /etc/nginx/ssl/example.com.key:
acme.sh --issue -d example.com -d www.example.com --webroot /var/www/html \
--install-cert -d example.com \
--key-file /etc/nginx/ssl/example.com.key \
--fullchain-file /etc/nginx/ssl/example.com.pem \
--reloadcmd "nginx -s reload"
注意:--install-cert 不是“只装一次”,它会把续期动作也绑定进去 —— 后续所有 acme.sh --renew 都会自动执行上面三个参数。
-
--reloadcmd必须用绝对路径调用nginx(如/usr/sbin/nginx -s reload),否则 crontab 下可能找不到命令 - 如果 Nginx 运行用户不是 root,需确保
/etc/nginx/ssl/目录对该用户可读,且私钥权限为600 - 首次运行后检查
/etc/nginx/ssl/下文件是否生成,并手动执行一次nginx -t && nginx -s reload验证配置有效性
让 crontab 每天凌晨 3 点自动续期
acme.sh 内置的定时任务(acme.sh --install-cronjob)会往 root 用户的 crontab 写入一行,但容易被其他工具覆盖或权限干扰。更可控的做法是手动加:
sudo crontab -e
添加这行:
0 3 * * * "/root/.acme.sh/acme.sh" --renew --force --quiet
-
--renew会检查证书剩余有效期,仅当 ≤ 30 天时才真正执行续期(避免频繁请求被限流) -
--force强制检查,配合--quiet可抑制日志刷屏,适合生产环境 - 不要用
*/12或*/6这类高频周期 —— Let's Encrypt 对同一域名每小时最多验证 5 次,误触可能进黑名单
最容易被忽略的两个坑
一是 HTTP 验证路径权限不对:/var/www/html/.well-known/acme-challenge/ 必须对 world-readable(至少 755),否则 CA 请求返回 403,续期静默失败;二是 Nginx 的 location ^~ /.well-known/acme-challenge/ 块必须放在所有 location ~ \.php$ 或 rewrite 规则之前,否则 PHP 脚本拦截了验证请求,返回 200 但内容为空 —— 这种错误不会报错,只会卡在“验证超时”,查日志也只看到 Connection refused。











