使用acme.sh自动化部署HTTPS证书:从原理到Nginx/Apache实战

发布时间:2026/7/4 10:09:35
使用acme.sh自动化部署HTTPS证书:从原理到Nginx/Apache实战 1. 项目概述从HTTP到HTTPS的必经之路如果你自己折腾过网站不管是个人博客、测试环境还是一个小型应用肯定都遇到过那个烦人的“不安全”提示。浏览器地址栏里那个红色的小锁或者干脆就是“不安全”三个字看着就让人心里不踏实。这背后就是HTTP协议在裸奔所有数据都以明文传输密码、会话信息、个人数据都暴露在风险之下。而解决这个问题的标准答案就是为你的网站部署TLS/SSL证书启用HTTPS。手动申请商业证书流程繁琐、价格不菲而且每年都得续期对于个人开发者或小项目来说是个不小的负担。好在有Let‘s Encrypt这样的公益CA证书颁发机构它提供免费的、自动化的证书颁发服务。而acme.sh就是与Let‘s Encrypt交互帮你自动化完成证书申请、验证、续期的神器。它纯Shell脚本编写依赖极少几乎能在任何Linux/Unix环境下运行是运维圈里公认的“懒人”必备工具。今天要聊的就是如何用acme.sh这把瑞士军刀为你的Nginx或Apache服务器全自动地搞定TLS证书的整个生命周期——从申请、安装到自动续期。整个过程你只需要关注域名所有权验证剩下的脏活累活acme.sh都会在后台默默帮你处理好。无论你是刚接触服务器配置的新手还是想优化现有流程的老手这套方案都能让你告别手动操作的麻烦实现真正意义上的“一次配置终身受用”。2. 核心原理与工具选型解析2.1 TLS/SSL与HTTPS不只是加把锁很多人把HTTPS简单理解为“给网站加把锁”这个比喻对但不完全。TLS传输层安全协议SSL的后继者的核心是建立一条加密的、可信的通信通道。它主要解决三个问题加密防止数据在传输过程中被窃听。完整性防止数据在传输过程中被篡改。身份验证确保你连接的是真正的目标服务器而不是一个钓鱼网站。证书在这里扮演着“数字身份证”的角色。它由受信任的CA签发里面包含了你的域名、公钥、签发者等信息。当浏览器访问你的HTTPS站点时会校验证书的有效性是否过期、是否由可信CA签发、域名是否匹配验证通过后才会用证书里的公钥开始加密通信的握手流程。Let‘s Encrypt的伟大之处在于它通过ACME协议自动化证书管理环境将证书的申请和续期完全自动化。ACME协议定义了一套客户端如acme.sh和CA服务器Let‘s Encrypt之间的交互流程核心是“域名控制权验证”。你必须向CA证明你拥有要申请证书的域名通常有两种主流方式HTTP-01挑战在你的网站根目录下放置一个由CA指定的特定文件CA会通过HTTP访问这个文件来验证。DNS-01挑战在你的域名DNS解析记录里添加一条特定的TXT记录CA会查询这条记录来验证。2.2 为什么是acme.sh市面上ACME客户端不少比如Certbot、acme.sh、lego等。我选择acme.sh是基于多年实战下来的几点考量零依赖纯Shell它就是一个Shell脚本不需要Python、Go等特定语言环境。下载即用几乎兼容所有Linux发行版和BSD系统甚至在OpenWRT路由器上都能完美运行。这对于维护大量不同环境服务器的运维来说省去了统一管理依赖的麻烦。配置即生效acme.sh的设计哲学是“无侵入”。它不会修改你的系统全局配置如/etc目录下的文件。所有证书、账户信息都存放在用户主目录下的~/.acme.sh/里。这意味着你可以用普通用户权限运行它安全性和灵活性更高。强大的自动化能力不仅仅是申请证书。它内置了将证书安装到Nginx/Apache配置目录并重载服务的命令。更重要的是它通过cron job实现了完全自动化的续期你几乎可以忘了证书过期这回事。广泛的DNS API支持如果你使用HTTP-01挑战需要网站80或443端口可被外部访问。对于没有公网IP的内网服务或者不想开放端口的场景DNS-01挑战是唯一选择。acme.sh支持Cloudflare、阿里云、腾讯云DNSPod、GoDaddy等几十家DNS服务商的API可以自动帮你添加和删除验证用的TXT记录实现了真正的“无人值守”申请。注意选择验证方式时HTTP-01最简单但需要域名能解析到你的服务器且对应端口开放。DNS-01最通用尤其适合泛域名证书*.example.com的申请但需要配置DNS服务商的API密钥有一定门槛。2.3 Nginx vs Apache证书安装的细微差别虽然最终目的都是让Web服务器加载证书但Nginx和Apache的配置文件语法和证书加载方式有区别acme.sh的安装命令也针对两者做了优化。Nginx通常将证书路径直接配置在虚拟主机server块的ssl_certificate和ssl_certificate_key指令中。acme.sh的--install-cert命令会帮你把证书和密钥复制到指定路径如/etc/nginx/ssl/但不会自动修改你的nginx.conf文件。你需要手动或通过脚本更新配置并重载Nginx。Apache配置通常分散在VirtualHost块中的SSLCertificateFile和SSLCertificateKeyFile指令。acme.sh的处理方式与Nginx类似。关键在于acme.sh负责的是证书文件的部署Web服务器配置的联动需要你根据自身架构来处理。对于使用Docker、K8s或配置管理工具Ansible的环境可以将证书文件作为数据卷或配置映射灵活性更高。3. 实战从零开始部署acme.sh并颁发证书3.1 环境准备与acme.sh安装假设我们有一台运行Ubuntu 22.04的服务器域名是example.com并且已经指向了这台服务器的公网IP。我们将使用HTTP-01挑战方式因为它最直观。首先通过SSH登录你的服务器。acme.sh的安装简单到令人发指# 切换到有sudo权限的普通用户不建议直接使用root ssh your_useryour_server_ip # 使用curl下载安装脚本并执行 curl https://get.acme.sh | sh -s emailmyexample.com这个命令做了几件事从GitHub下载acme.sh安装脚本。将脚本输出通过管道|传递给sh执行。-s emailmyexample.com将你的邮箱地址作为参数传递给脚本这个邮箱用于接收证书到期提醒和ACME账户注册。安装完成后脚本会自动在你的用户主目录下创建~/.acme.sh/目录所有相关文件都在这里。同时它会把acme.sh命令添加到当前用户的Shell环境变量中通常通过修改~/.bashrc或~/.profile。你需要新开一个终端窗口或者执行source ~/.bashrc来让命令生效。实操心得安装过程可能会因为网络问题从GitHub下载失败。如果遇到可以尝试使用国内的镜像源比如先git clone https://gitee.com/neilpang/acme.sh.git然后进入目录执行./acme.sh --install -m myexample.com。另外邮箱地址务必填写真实有效的这是Let‘s Encrypt账户的唯一标识。3.2 使用HTTP-01挑战颁发证书现在我们来为example.com和www.example.com申请一张证书。确保你的Nginx或Apache已经配置好并且可以通过http://example.com正常访问显示默认页面或你的网站。# 使用standalone模式。acme.sh会临时监听80端口来完成验证。 # 注意这需要你的服务器80端口没有被占用如Nginx/Apache正在运行。 # 如果80端口已被占用需要先停止Web服务或者使用下文提到的webroot模式。 sudo systemctl stop nginx # 或 apache2, httpd # 执行证书颁发命令 acme.sh --issue -d example.com -d www.example.com --standalone命令解析--issue表示执行颁发证书的操作。-d指定域名可以多次使用来申请多域名证书SAN证书。--standalone使用standalone模式进行HTTP-01挑战。acme.sh会自己启动一个临时的HTTP服务器在80端口等待Let‘s Encrypt的验证请求。执行后你会看到一串输出acme.sh会自动完成与Let‘s Encrypt的通信、域名验证、证书签发和下载的过程。成功后证书文件会保存在~/.acme.sh/example.com/目录下。更优雅的webroot模式停止Web服务毕竟影响线上业务。更常用的方法是webroot模式它利用你正在运行的Web服务器来提供验证文件。# 假设你的网站根目录是 /var/www/html # 先启动你的Web服务 sudo systemctl start nginx # 使用 --webroot 或 -w 参数指定网站根目录 acme.sh --issue -d example.com -d www.example.com -w /var/www/html在这个模式下acme.sh会在/var/www/html/.well-known/acme-challenge/目录下创建验证文件Let‘s Encrypt的验证服务器会通过HTTP访问这个文件。你需要确保你的Nginx/Apache配置允许访问.well-known目录。通常一个通用的配置片段如下Nginx示例location ^~ /.well-known/acme-challenge/ { alias /var/www/html/.well-known/acme-challenge/; try_files $uri 404; }注意事项webroot模式是最推荐的生产环境用法。确保-w参数指定的路径是你的Web服务器实际提供网站内容的根目录。对于拥有多个虚拟主机的服务器你需要为每个域名指定正确的webroot路径。3.3 使用DNS-01挑战颁发泛域名证书如果你的域名托管在Cloudflare、阿里云等支持API的服务商并且你需要一张泛域名证书*.example.com那么DNS-01挑战是唯一选择。这里以Cloudflare为例。获取Cloudflare API Token 登录Cloudflare控制台进入“我的个人资料” - “API令牌” - 创建令牌。选择“编辑区域 DNS”模板选择需要管理的域名然后创建令牌。复制生成的API Token。在服务器上设置API凭证acme.sh使用环境变量来读取DNS API的密钥。将你的Cloudflare API信息导出为环境变量。export CF_Token你的API_Token export CF_Account_ID你的账户ID在域名概述页面右侧找到 # 如果是全局API Key不推荐则使用 CF_Key 和 CF_Email # export CF_Key你的Global_API_Key # export CF_Email你的Cloudflare登录邮箱颁发泛域名证书acme.sh --issue --dns dns_cf -d example.com -d *.example.com参数--dns dns_cf告诉acme.sh使用Cloudflare的DNS API进行验证。执行后脚本会自动在Cloudflare上为你的域名添加一条临时的TXT记录来完成验证验证成功后会自动删除该记录。踩坑记录DNS API的Token或Key权限一定要给对。对于Cloudflare使用Zone级别的TokenEdit zone DNS比全局API Key更安全。另外环境变量是临时的如果希望永久保存可以将export命令添加到你的~/.bashrc或~/.profile文件中或者使用acme.sh的--install-cert命令时指定--cert-file等参数它会在续期时自动读取这些环境变量。最安全的方式是使用acme.sh的--config-home参数指定一个安全目录并将凭证文件存放在那里。4. 将证书安装到Nginx或Apache证书申请好了但还在~/.acme.sh/目录里。我们需要把它放到Web服务器通常寻找证书的位置并配置服务使用它。4.1 为Nginx安装证书Nginx通常期望证书和密钥文件在/etc/nginx/ssl/或类似目录。我们先创建这个目录并设置正确的权限。sudo mkdir -p /etc/nginx/ssl/example.com sudo chmod 755 /etc/nginx/ssl使用acme.sh的--install-cert命令来安装证书。这个命令的优势在于它会在证书续期后自动重新安装并执行你指定的重载命令。acme.sh --install-cert -d example.com \ --key-file /etc/nginx/ssl/example.com/private.key \ --fullchain-file /etc/nginx/ssl/example.com/fullchain.cer \ --reloadcmd sudo systemctl reload nginx命令解析--key-file指定私钥文件的存放路径。--fullchain-file指定完整证书链文件的存放路径。这个文件包含了你的站点证书和中间CA证书Nginx的ssl_certificate指令需要这个。--reloadcmd指定在证书安装或续期后要执行的命令。这里是重载Nginx配置不是重启使新证书生效。现在配置你的Nginx虚拟主机来使用这些证书。编辑你的站点配置文件如/etc/nginx/sites-available/example.comserver { listen 443 ssl http2; listen [::]:443 ssl http2; server_name example.com www.example.com; # 指定证书路径指向acme.sh安装的位置 ssl_certificate /etc/nginx/ssl/example.com/fullchain.cer; ssl_certificate_key /etc/nginx/ssl/example.com/private.key; # 以下是一些推荐的SSL优化配置 ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d; # 你的其他配置如root目录、index文件、代理规则等 root /var/www/html; index index.html index.htm; } # 强制HTTP跳转到HTTPS可选但推荐 server { listen 80; listen [::]:80; server_name example.com www.example.com; return 301 https://$server_name$request_uri; }检查Nginx配置语法并重载服务sudo nginx -t sudo systemctl reload nginx4.2 为Apache安装证书Apache的流程类似。首先创建证书目录sudo mkdir -p /etc/apache2/ssl/example.com sudo chmod 755 /etc/apache2/ssl使用acme.sh安装证书到Apache的目录acme.sh --install-cert -d example.com \ --key-file /etc/apache2/ssl/example.com/private.key \ --fullchain-file /etc/apache2/ssl/example.com/fullchain.cer \ --reloadcmd sudo systemctl reload apache2然后启用Apache的SSL模块并配置虚拟主机sudo a2enmod ssl sudo a2enmod headers # 通常用于HSTS等安全头编辑你的Apache站点配置文件如/etc/apache2/sites-available/example.com-ssl.confIfModule mod_ssl.c VirtualHost *:443 ServerName example.com ServerAlias www.example.com # 指定证书路径 SSLEngine on SSLCertificateFile /etc/apache2/ssl/example.com/fullchain.cer SSLCertificateKeyFile /etc/apache2/ssl/example.com/private.key # 其他配置 DocumentRoot /var/www/html Directory /var/www/html Options Indexes FollowSymLinks AllowOverride All Require all granted /Directory # 推荐的安全头 Header always set Strict-Transport-Security max-age63072000; includeSubDomains /VirtualHost /IfModule启用SSL站点配置并重载Apachesudo a2ensite example.com-ssl.conf sudo apache2ctl configtest sudo systemctl reload apache2核心技巧--install-cert命令中的--reloadcmd参数是自动续期的关键。acme.sh会创建一个定时任务cron job每天检查证书是否即将到期默认提前30天。如果快到期了它会自动续期然后调用这个reloadcmd来重载Web服务整个过程无需人工干预。你可以通过crontab -l命令查看当前用户下的这个定时任务。5. 高级配置与优化要点5.1 证书类型与密钥算法选择默认情况下acme.sh使用RSA 2048位密钥和ECC P-256椭圆曲线证书。但为了更好的安全性和性能特别是对现代浏览器的兼容性可以考虑使用ECC证书。# 在颁发证书时指定使用ECC密钥 acme.sh --issue -d example.com --standalone --keylength ec-256 # 或者使用更安全的 ec-384 # acme.sh --issue -d example.com --standalone --keylength ec-384ECC证书体积更小计算速度更快安全性在相同密钥长度下比RSA更高。但需要注意一些非常古老的客户端或设备可能不支持ECC。对于绝大多数现代网站ec-256是完全够用且推荐的。5.2 证书的备份与迁移~/.acme.sh/目录是你的证书仓库。里面最重要的文件是example.com/example.com.cer你的站点证书。example.com/example.com.key你的私钥。example.com/fullchain.cer完整的证书链站点证书中间证书。example.com/ca.cer中间证书。备份定期备份整个~/.acme.sh/目录或者至少备份对应域名目录下的.key和.cer/.conf文件。私钥一旦丢失证书就无法使用。迁移如果你要将证书迁移到新服务器只需将~/.acme.sh/目录复制过去并确保在新服务器上安装了相同版本的acme.sh。然后重新执行--install-cert命令路径可能需要调整并设置好--reloadcmd。账户信息在~/.acme.sh/account.conf中也会一并迁移无需重新注册。5.3 配置自动化与持续集成在容器化或CI/CD环境中你可能不希望手动登录服务器操作。可以将acme.sh的安装和证书申请过程脚本化。一个简单的Dockerfile示例用于构建一个包含有效证书的Nginx镜像FROM nginx:alpine # 安装acme.sh的依赖和acme.sh本身 RUN apk add --no-cache curl openssl socat \ curl https://get.acme.sh | sh -s emailciexample.com # 复制一个脚本用于在容器启动时申请证书需要外部挂载webroot或设置DNS API环境变量 COPY init-cert.sh /usr/local/bin/ RUN chmod x /usr/local/bin/init-cert.sh # 将acme.sh的安装目录添加到PATH ENV PATH/root/.acme.sh:${PATH} # 默认的Nginx配置包含SSL配置占位符 COPY nginx.conf /etc/nginx/nginx.conf CMD [/usr/local/bin/init-cert.sh]init-cert.sh脚本需要包含申请证书、安装证书到容器内路径、并启动Nginx的逻辑。关键在于证书的验证数据webroot目录或DNS API密钥需要从外部如Docker Secret、环境变量文件提供给容器。重要提醒在CI/CD流水线中自动化证书申请时务必妥善保管你的ACME账户私钥和DNS API令牌。这些敏感信息应存储在安全的密钥管理服务如Hashicorp Vault、AWS Secrets Manager或CI系统的安全变量中绝不能硬编码在脚本或Dockerfile里。6. 故障排查与常见问题实录即使流程再清晰实际操作中难免会遇到问题。下面是我在无数次部署中积累下来的常见错误和解决方法。6.1 证书申请失败问题1acme.sh报错Create new order error. Le_OrderFinalize not found或类似网络错误。原因通常是网络连接问题无法访问Let‘s Encrypt的API服务器默认是生产环境acme-v02.api.letsencrypt.org。排查使用curl -v https://acme-v02.api.letsencrypt.org/directory测试连通性。如果服务器在国内可能会遇到连接不稳定或超时。可以尝试切换CA服务器到Let‘s Encrypt的备用端点或者使用ZeroSSLacme.sh也支持。# 切换CA到Let‘s Encrypt的v2备用端点不保证可用性 acme.sh --set-default-ca --server letsencrypt # 或者切换到ZeroSSL需要先注册邮箱 acme.sh --set-default-ca --server zerossl acme.sh --register-account -m myexample.com --server zerossl问题2HTTP-01挑战失败提示Verify error:Invalid response from https://example.com/.well-known/acme-challenge/...原因验证文件无法通过HTTP被公开访问。排查检查你的Web服务器Nginx/Apache是否正在运行并且80端口是否开放sudo netstat -tulpn | grep :80。检查防火墙如ufw, firewalld是否允许80端口入站流量。检查Web服务器配置中.well-known/acme-challenge/路径的访问是否正确映射。确保没有其他location规则比如处理PHP的location ~ \.php$拦截了这个路径的请求。尝试手动访问验证URL。在申请过程中acme.sh会输出一个类似https://example.com/.well-known/acme-challenge/xxxx的URL。用curl或浏览器访问它看是否能下载到一个简单的文本文件。问题3DNS-01挑战失败提示Error add txt for domain:_acme-challenge.example.com原因DNS API调用失败通常是API凭证错误、权限不足或网络问题。排查仔细核对API凭证对于Cloudflare确认使用的是Zone级别的Token并且Token拥有编辑DNS的权限。账户IDCF_Account_ID也要填对。检查域名解析确保你要申请证书的域名如example.com确实由你配置了API的DNS服务商管理。手动测试API可以写一个简单的curl命令来测试API是否正常工作。例如对于Cloudflare可以尝试列出区域DNS记录。查看详细日志在acme.sh命令后添加--debug或--log参数会输出更详细的调试信息有助于定位问题。6.2 证书安装后Nginx/Apache报错问题Nginx启动失败错误日志显示SSL_CTX_use_PrivateKey_file或SSL_CTX_use_certificate_chain_file failed原因证书文件路径错误、权限不足或文件格式不对。排查检查路径确认Nginx配置中ssl_certificate和ssl_certificate_key指令指向的路径与acme.sh --install-cert命令中--fullchain-file和--key-file指定的路径完全一致。检查权限证书和密钥文件需要对运行Nginx的用户通常是www-data或nginx可读。确保私钥文件.key的权限是600-rw-------。sudo chmod 600 /etc/nginx/ssl/example.com/private.key sudo chown root:root /etc/nginx/ssl/example.com/private.key # 所有权归root # Nginx进程以www-data用户运行只要root可读Nginx通过master进程root启动读取后传递给worker进程是没问题的。检查文件内容使用cat或openssl命令检查文件内容是否正确。# 检查私钥 openssl rsa -in /etc/nginx/ssl/example.com/private.key -noout -text # 检查证书 openssl x509 -in /etc/nginx/ssl/example.com/fullchain.cer -noout -text如果fullchain.cer文件内容看起来像两个证书拼接在一起的通常以-----BEGIN CERTIFICATE-----开头和结尾那就是正确的。6.3 自动续期不工作问题证书快过期了但没有自动续期。原因acme.sh的自动续期依赖于cron job。可能cron服务未运行或者acme.sh的安装脚本没有成功添加定时任务。排查检查cron服务状态sudo systemctl status cron(Ubuntu/Debian) 或sudo systemctl status crond(CentOS/RHEL)。查看当前用户的cron任务crontab -l。你应该能看到类似这样的行0 0 * * * /home/username/.acme.sh/acme.sh --cron --home /home/username/.acme.sh /dev/null如果没有可以手动添加或者重新运行acme.sh的安装。手动测试续期执行acme.sh --cron或acme.sh --renew -d example.com来手动触发续期流程观察输出是否有错误。6.4 证书链不完整导致浏览器警告问题网站显示HTTPS锁是灰色的或者提示“证书链不完整”。原因Web服务器没有提供完整的证书链。浏览器需要从你的站点证书追溯到它信任的根证书。如果缺少中间证书浏览器可能无法验证。解决方案这正是我们一直使用--fullchain-file的原因。acme.sh生成的fullchain.cer文件已经包含了站点证书和必要的中间证书。确保你的Nginx配置中ssl_certificate指令指向的是fullchain.cer文件而不是单独的example.com.cer文件。Apache的SSLCertificateFile也同样指向fullchain.cer。下表总结了最常见的问题和快速解决思路问题现象可能原因快速排查步骤申请失败网络错误连接不到CA服务器1.curl -v测试CA API地址2. 尝试切换CA服务器--serverHTTP验证失败验证文件无法被公开访问1. 检查Web服务与端口2. 检查防火墙3. 手动访问验证URLDNS验证失败DNS API配置错误1. 核对API Token/Key和权限2. 确认域名由该DNS服务商管理3. 使用--debug模式查看详细日志Nginx/Apache启动报SSL错证书文件问题1. 检查配置文件路径是否正确2. 检查证书文件权限私钥6003. 用openssl命令检查文件格式浏览器提示不安全证书链不完整确认配置中使用的是fullchain.cer文件没有自动续期cron任务未生效1. 检查cron服务状态2. 执行crontab -l查看任务3. 手动运行acme.sh --cron测试最后一个我个人的习惯是在每次通过acme.sh成功操作后都会去SSL检测网站如 SSL Labs Server Test 跑一下测试。它能给出非常详细的安全评级和配置建议比如支持的协议、加密套件、是否启用HSTS等帮助你进一步优化HTTPS配置确保站点的安全性达到最佳实践。整个过程从申请、安装到优化、排错虽然细节不少但一旦跑通并自动化你就会发现维护HTTPS证书从此变成了一件毫无存在感的背景任务而这正是高效运维所追求的状态。