发布作者: 大鱼
百度收录: 正在检测是否收录...
作品采用: 《 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 》许可协议授权
给站点上 HTTPS 只需要三步:申请证书、改 nginx 配置、安装生效。本文用 acme.sh 配合 DNS-01 验证向 Let's Encrypt 申请免费泛域名证书,不需要开放 80 端口,也不依赖 Python 环境。申请阶段给出安装、切换 CA、配置 DNS API 密钥与签发命令,并把泛域名参数为什么要加引号、密钥该用子账号这两处细节说清楚。配置阶段给出一份完整的 443 server 块和 80 跳转写法,并说明 fullchain 为什么不能换成 cert、协议版本该留哪些。安装阶段讲清一个最容易踩的坑:证书不能直接从 acme.sh 的目录里引用,必须用 install-cert 复制到固定路径并挂上 reloadcmd,否则续期之后 nginx 拿到的还是旧证书,而文件时间看着又是新的。
acme.sh 去 Let's Encrypt 换回证书文件,配置是告诉 nginx 从哪个路径读,安装是把证书复制到那个路径、让配置真正加载,并对外开放 443;三步里最容易翻车的是第三步,证书复制了但没挂 reloadcmd,三个月后照样过期,查文件时间还查不出问题。acme.sh 是纯 shell 脚本、不依赖 Python,装完就一个目录,建议直接用 root 安装、后面所有命令都在 root 下执行,原因是 acme.sh 装在哪个用户的家目录、定时任务就属于谁,用 sudo 跨用户调用会找不到它;装完会在 ~/.acme.sh/ 下生成目录并自动写入一条 cron,每天检查一次证书是否临近到期,当前 shell 需要重新登录或 source ~/.bashrc 才能直接调用 acme.sh 命令;接着把默认 CA 换成 Let's Encrypt,acme.sh 从 3.0 起默认用 ZeroSSL、两者都能用,但 Let's Encrypt 的资料和工具兼容性更好、出问题时更容易查,这一步不做签出来的就是 ZeroSSL 证书;然后配置 DNS 服务商的 API 密钥,以阿里云为例就是 Ali_Key 和 Ali_Secret 两个变量,它们只在当前会话有效、acme.sh 首次签发后会把值记进配置文件,后续续期不再需要重新设置,密钥建议单独建一个子账号、只授予 DNS 解析权限,不要用主账号的完整密钥;签发时 -d 可以跟多个域名,主域名和泛域名要分别写一条,泛域名那条必须加引号、不加的话 shell 会把 * 当成通配符自行展开,dns_ali 是阿里云对应的插件名,换成 Cloudflare 就是 dns_cf 配 CF_Token,换成腾讯云 DNSPod 就是 dns_dp 配 DP_Id 和 DP_Key;DNS-01 的原理是 acme.sh 调服务商 API 加一条 _acme-challenge 的 TXT 记录,等 Let's Encrypt 从公网查到这个值、验证通过后再把记录删掉,整个过程 80 端口完全用不到、域名也不必解析到本机,甚至可以在另一台机器上签好再传过去;签发成功后文件在 ~/.acme.sh/example.com_ecc/ 下,fullchain.cer 是完整证书链、example.com.key 是私钥,默认签的是 ECC 证书,体积小、握手快,现代浏览器全部支持。# 安装
curl https://get.acme.sh | sh -s email=you@example.com
source ~/.bashrc
# 切换 CA
acme.sh --set-default-ca --server letsencrypt
# 配置 DNS 密钥(阿里云)
export Ali_Key="你的AccessKeyId"
export Ali_Secret="你的AccessKeySecret"
# 签发主域名 + 泛域名
acme.sh --issue --dns dns_ali -d example.com -d '*.example.com'80 服务块复制一份改成 443、加上证书相关的指令,location 部分原样保留;证书两行写的是 /etc/nginx/ssl/ 下的路径,这是后面 install-cert 要复制过去的位置,不要直接写 ~/.acme.sh/...,原因在安装那一节说明;http2 on; 是 1.25.1 之后的新语法、老版本要写成 listen 443 ssl http2;,用新语法在旧版本上会报未知指令;ssl_protocols 只留 TLSv1.2 和 TLSv1.3,TLSv1 与 TLSv1.1 已被主流浏览器标记为不安全、开着不仅没用还会被安全评级扣分;ssl_ciphers 给出一组安全套件、!aNULL 排除匿名认证、!MD5 排除弱散列,ssl_prefer_server_ciphers off 看着反直觉,但这正是官方现在的推荐值,现代客户端的套件排序更合理、服务端强行指定反而可能选到更慢的组合;会话缓存两行让同一客户端在有效期内复用 TLS 会话、省掉完整握手,对首屏有实际影响,10m 共享内存大约能存四万个会话、普通站点足够;原来的 80 服务块改成纯跳转,用 301 而不是 302、这是永久规则、浏览器会缓存,后续请求直接走 https、少一次往返;$request_uri 必须带上,它包含原始路径和查询串、漏掉的话所有内页都会跳到首页;反向代理那几个请求头照旧要写,尤其 X-Forwarded-Proto,后端靠它判断当前是不是 https,漏了会出现登录后跳回 http、回调地址拼错这类问题,而页面本身完全正常、很难往这个方向想。server {
listen 443 ssl;
http2 on;
server_name app.example.com;
ssl_certificate /etc/nginx/ssl/fullchain.cer;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 80;
server_name app.example.com;
return 301 https://$host$request_uri;
}install-cert 把证书复制过去并登记重载命令;这条命令做两件事,把证书和私钥复制到指定路径,并记住「续期成功后执行 systemctl reload nginx」,acme.sh 在证书剩余三十天左右自动续期、续完立刻执行 reloadcmd、nginx 当场换上新证书,所以不需要额外写 deploy hook 脚本、也不用改 crontab;不能省掉复制直接引用 ~/.acme.sh/,是因为续期时会重写该目录下的文件、路径和文件名都可能变化,nginx 里写死那个路径早晚会失效,官方也明确要求用 install-cert 输出到固定位置,这是唯一受支持的用法;--ecc 不能漏,默认签的是 ECC 证书,不加这个参数会去找 RSA 目录、报找不到证书。mkdir -p /etc/nginx/ssl
acme.sh --install-cert -d example.com --ecc \
--key-file /etc/nginx/ssl/example.com.key \
--fullchain-file /etc/nginx/ssl/fullchain.cer \
--reloadcmd "systemctl reload nginx"Ubuntu 用 ufw 放行,CentOS 用 firewall-cmd 加上 https 服务再重载规则;云服务器还要在控制台安全组放行 443,这一步漏掉是最常见的「证书装好了但打不开」,现象是浏览器直接超时、而不是报证书错误。# Ubuntu
sudo ufw allow 443/tcp
# CentOS
sudo firewall-cmd --add-service=https --permanent
sudo firewall-cmd --reloadnginx -t 会校验证书文件是否存在、路径对不对,路径写错时直接指到行号;第一条 curl 返回 200 说明证书已生效,第二条应返回 301、Location 指向 https;想确认证书链是否完整用 openssl 那条,输出里能看到到期日和签发者,链完整时不会出现 unable to verify 之类的提示。nginx -t
curl -I https://app.example.com
curl -I http://app.example.com
openssl s_client -connect app.example.com:443 -servername app.example.com </dev/null 2>/dev/null | openssl x509 -noout -dates -issueracme.sh 的定时任务在安装时就写好了,用 crontab 那条确认它还在;想验证 reloadcmd 是否真的生效可以强制续一次,它会真的走一遍签发流程、并在续完立刻重载;注意 Let's Encrypt 对同一组域名有每周五次的签发上限,--force 别反复跑,试一次确认流程通了就停手,日常维护只需要看定时任务在、证书能正常访问,剩下的交给它。crontab -l | grep acme
acme.sh --renew -d example.com --force --ecc
—— 评论区 ——