今天一大早,微信群里又有外贸老板在叫唤,说海外客户发邮件投诉网站打不开,提示“您的连接不是私密连接”。我登上去一看,得,Let's Encrypt 的证书昨天到期,自动续期根本没跑成功。做外贸站或者内容站这几年,这种破事我见得太多了。今天这篇 SSL证书自动续期失效排坑 实战指南,就是写给那些被 Certbot 或 acme.sh 搞得心态爆炸的朋友。不讲空话,全是线上踩坑踩出来的经验,建议收藏备用。
这是新手站长最常犯的低级错误。为了所谓的 SEO 权重集中,很多人一上来就在 Nginx 里写全站 HTTP 强跳 HTTPS:
server { listen 80; server_name yourdomain.com www.yourdomain.com; return 301 https://$host$request_uri; }
问题出在哪?
Let's Encrypt 默认的 HTTP-01 验证方式,需要 CA 服务器访问你网站的 http://yourdomain.com/.well-known/acme-challenge/xxx 这个路径来验证域名所有权。虽然现在的验证机制允许重定向,但如果你的 HTTPS 配置本身就过期了、或者中间被防火墙、CDN 给劫持了,验证请求就会死在跳转链里,直接报错 Invalid response from...。
解决办法:
在 80 端口的配置里,把验证路径单独拎出来,绝对不要让它被重定向干扰:
server { listen 80; server_name yourdomain.com www.yourdomain.com; # 专门放行 acme 挑战路径,不走重定向 location ^~ /.well-known/acme-challenge/ { default_type "text/plain"; root /var/www/certbot; } # 其他流量再走 301 location / { return 301 https://$host$request_uri; } }
做外贸站的,十个里面有九个套了 Cloudflare CDN。很多人直接在本地服务器跑自动续期,结果每次到期前都静悄悄地失败。
核心死穴:
Flexible,你源站没有证书,但 CDN 显示正常,一旦切换模式全站立刻 526 报错。老站长推荐解法:改用 DNS-01 验证(终极解决方案)
别再用脆弱的 HTTP 文件验证了,换成 DNS API 验证,根本不需要暴露 80 端口,直接通过 Cloudflare API 修改 DNS TXT 记录完成验证,百发百中。
如果你用的是 acme.sh,直接三行命令解决:
export CF_Key="你的Global_API_Key或者Token" export CF_Email="你的Cloudflare邮箱" # 自动生成证书并配置自动续期 acme.sh --issue --dns dns_cf -d yourdomain.com -d *.yourdomain.com
这种方案连通配符证书(泛域名)一起搞定,还不用怕 CDN 拦截。
很多站长看日志,发现系统明明提示 Certificate renewed successfully,但用浏览器访问还是显示证书过期。半夜起来提着刀找服务器算账。
真相往往很滑稽:
Certbot 或者脚本把 .pem 文件更新到了磁盘里,但你的 Nginx 或 Apache 是常驻内存的,它不会自己去磁盘重新读文件!
你必须在续期命令后面挂上重载服务的钩子(Deploy Hook)。
certbot renew --quiet --deploy-hook "nginx -s reload"acme.sh --install-cert -d yourdomain.com \ --key-file /etc/nginx/ssl/yourdomain.key \ --fullchain-file /etc/nginx/ssl/fullchain.cer \ --reloadcmd "systemctl reload nginx"你在终端手动敲命令,次次都是 Success;一丢给系统的 crontab 跑,到期必定扑街。
坑点剖析:
Linux 的 cron 默认环境变量极简,它的 $PATH 通常只有 /usr/bin:/bin。而你的 certbot 或者 acme.sh 可能装在 /usr/local/bin 或者 ~/.acme.sh/ 目录下。定时任务一触发,直接报 command not found,默默挂掉。
排查与修正:
# 错误写法:纯碰运气 0 2 * * * certbot renew # 正确写法:输出到日志排错 0 2 * * * /usr/bin/certbot renew --deploy-hook "systemctl reload nginx" >> /var/log/certbot-renew.log 2>&1
过一个月去查看下 /var/log/certbot-renew.log,到底卡在哪一步,黑底白字清清楚楚。
有时候配置没调对,新手喜欢疯狂执行续期命令测试。几分钟内连试十几次,接着就会看到冰冷的错误:too many failed authorizations recently。
Let's Encrypt 对单个域名每个小时、每周都有极其严格的请求限制。一旦被限流,你除了干等几天没有任何办法(除非换别的 CA)。
正确姿势:
调试时必须加 --dry-run 参数!这代表走测试沙盒环境,不消耗正式限额:
certbot renew --dry-run
只有当终端吐出 Simulated renewal succeeded 时,才证明你的网络、配置、重定向完全没毛病,这时候再去跑正式命令。
服务器环境错综复杂,一次系统自动更新 openssl、一次改动 Nginx 结构,都有可能导致续期挂掉。
靠人肉记日历去查证书到期是下下策,指望自动续期万无一失也有点天真。作为老手,我的常规操作是配合三方监控工具(比如 UptimeRobot、自建的 Prometheus 或者甚至一个简短的 Python 脚本),设定好当网站证书剩余时间少于 15 天时,直接往 Telegram 或企业微信发报警。
把防御机制做到位,才能踏实睡个好觉,别等外贸订单因为一个破证书丢了,才想起来排查。