给站点上 HTTPS 这件事,十年前要买证书、填 CSR、等审核、一年一续。现在免费证书一条命令的事。
但 2026 年这块有两个新变化,很多教程还没更新:Let’s Encrypt 已经支持 160 小时(6 天)的超短证书和 IP 地址证书,默认证书寿命也正在从 90 天往 45 天降。这意味着以前那套"提前 30 天告警"的经验要重算了。
这篇从零开始,把 Nginx 上 HTTPS 配完,再把自动续期和 2026 年的新选项讲清楚。
环境准备
# Debian/Ubuntusudoaptupdatesudoaptinstall-ynginx certbot python3-certbot-nginx# 确认版本(要看 certbot 4.0+ 才支持 profile 选择)nginx-vcertbot--version配置前有两个前置条件,不满足的话 ACME 验证一定失败:
- 域名已经解析到这台机器,
dig yourdomain.com +short能查到你的公网 IP。 - 80 端口对公网开放,HTTP-01 验证要从外面访问
http://yourdomain.com/.well-known/acme-challenge/xxx。
云服务器的话,除了系统防火墙,别忘了安全组也要放行 80 和 443。
第一步:先让 HTTP 站点跑起来
这一步别跳过。证书是签给"已经能正常访问的域名"的,先确保 HTTP 通了,再动 HTTPS。
# /etc/nginx/conf.d/app.conf server { listen 80; server_name yourdomain.com; root /var/www/app; index index.html; }sudonginx-t&&sudosystemctl reload nginxcurl-Ihttp://yourdomain.com# 应该返回 200第二步:申请证书
用 webroot 方式,它对现有服务侵入最小:
sudomkdir-p/var/www/certbotsudocertbot certonly--webroot\-w/var/www/certbot\-dyourdomain.com-dwww.yourdomain.com\--emailyou@example.com\--agree-tos --no-eff-email\--deploy-hook"systemctl reload nginx"几个参数说明:
--webroot -w:certbot 把验证文件写进这个目录,Nginx 通过 /.well-known/ 路径把它暴露出去。--deploy-hook:签发成功后自动执行。这条很关键,不加重载钩子,证书换了 Nginx 也不会加载新证书,你会一直在用旧的。- 多个域名用多个
-d,同一张证书会覆盖所有域名(SAN)。
Nginx 里要加一条 location,让验证目录能访问:
location /.well-known/acme-challenge/ { root /var/www/certbot; }证书文件会生成在/etc/letsencrypt/live/yourdomain.com/下,其中fullchain.pem和privkey.pem是 Nginx 要用的两个。
第三步:配置 HTTPS 站点
server { listen 443 ssl; http2 on; server_name yourdomain.com www.yourdomain.com; ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d; ssl_session_tickets off; # OCSP stapling ssl_stapling on; ssl_stapling_verify on; resolver 223.5.5.5 8.8.8.8 valid=300s; resolver_timeout 5s; add_header Strict-Transport-Security "max-age=31536000" always; add_header X-Content-Type-Options nosniff always; add_header X-Frame-Options SAMEORIGIN always; add_header Referrer-Policy strict-origin-when-cross-origin always; root /var/www/app; index index.html; location /.well-known/acme-challenge/ { root /var/www/certbot; } } # HTTP 全量跳 HTTPS server { listen 80; server_name yourdomain.com www.yourdomain.com; location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { return 301 https://$host$request_uri; } }几个点解释一下。
http2 on;是 Nginx 1.25.1 之后的新写法,以前是listen 443 ssl http2,老写法已经废弃了,会报 warning。
HSTS 别一上来就加includeSubDomains和preload。max-age=31536000意味着浏览器接下来一年强制走 HTTPS,如果哪天证书出问题、或者有子域名还是 HTTP,用户会被彻底卡住打不开。先跑一两周确认没问题,再考虑加预加载。
只开TLSv1.2 TLSv1.3就够了,1.0 和 1.1 早该淘汰。
改完验证:
sudonginx-tsudosystemctl reload nginxcurl-Ihttps://yourdomain.com第四步:确认自动续期真的在工作
这是最容易"配置完了但没验证"的一步。很多人的证书都是某天静默过期,然后站点全天报证书错误。
# 干跑一次,看续期流程会不会成功sudocertbot renew --dry-run# 确认定时任务存在systemctl list-timers|grepcertbotcertbot 装好时会自动加一个 systemd timer(老系统是 cron),每天跑两次,检查有没有证书快到期。这个定时任务是随机的、带偏移的,避免所有机器同一秒去请求 Let’s Encrypt。
看证书还剩多久:
sudocertbot certificates第五步:2026 年的新选项——要不要用短证书
这是今年最大的变化,值得单独说。
从 2026 年 1 月 15 日起,Let’s Encrypt 正式提供了 160 小时(刚好超过 6 天)的短证书,以及 IPv4/IPv6 的 IP 地址证书,两者都已 GA。短证书通过 ACME 的shortlivedprofile 申请。
为什么会有这个?核心原因是吊销机制不可靠。CRL 和 OCSP 这两个用来"作废证书"的东西,实际上很多浏览器根本不好好检查。所以一张私钥泄露的证书,在它过期之前可能一直被信任——最长 90 天。把寿命压到 6 天,这个窗口就被物理压缩了。
现在有三档可选:
| profile | 有效期 | 说明 |
|---|---|---|
classic | 90 天 | 默认,最稳 |
tlsserver | 45 天 | 已在推的路上 |
shortlived | 160 小时(约 6.7 天) | 可选,自动化必须完全可靠 |
用 certbot 申请短证书(需要 certbot 4.0+ 的 profile 支持):
sudocertbot certonly--webroot-w/var/www/certbot\-dyourdomain.com\--preferred-profile shortlived\--deploy-hook"systemctl reload nginx"我的建议:绝大多数人不要碰 6 天短证书。Let’s Encrypt 自己都说了"这不是给所有人用的"。算一笔账:6 天有效期意味着你的续期管道只要停摆大约 4 天,证书就过期了。周末出个故障,周一回来站点就是红锁。
真正值得你现在做的是另一件事:把 profile 设成tlsserver(45 天),当作自动化的压力测试。它已经在推的路上(Let’s Encrypt 计划几年内把默认寿命从 90 天降到 45 天),而且业内还有一个 2029 年要落地到 47 天的方案。现在就在 45 天节奏下跑通,等到政策真正切换时,你什么都不用改。
第六步:告别硬编码的续期时间
以前大家都记一条规则:“到期前 30 天续期”。这是 90 天证书时代的经验(90 天的三分之一就是 30 天)。
但证书寿命变成 45 天、47 天甚至 6 天之后,这个固定天数就没意义了——45 天的证书,"还剩 30 天"时离该续期还早得很;6 天的证书,30 天的阈值永远不会触发。
标准答案已经有了:ACME ARI(RFC 9773)。CA 会通过一个接口告诉你"该在哪个时间窗内续期",客户端拿到窗口后在窗口里随机选一个时间点执行。
# 客户端会去查询 renewalInfo 端点,获取建议的续期窗口sudocertbot renew --dry-run-v2>&1|grep-irenewal好消息是 certbot 已经内置支持 ARI,你不需要改任何配置,续期时间由服务端建议。坏消息是你的监控告警还停留在老阈值上。
所以最后一步要做的是:把"证书到期前 X 天告警"换成基于剩余比例或 ARI 的告警。写个最简单的脚本,每天检查剩余天数,低于有效期的三分之一就报警:
#!/bin/bash# check-cert.shDOMAIN="yourdomain.com"END=$(echo|openssl s_client-servername$DOMAIN-connect$DOMAIN:4432>/dev/null\|openssl x509-noout-enddate|cut-d=-f2)END_TS=$(date-d"$END"+%s)NOW_TS=$(date+%s)DAYS=$(((END_TS-NOW_TS)/86400))echo"$DOMAIN剩余$DAYS天"[$DAYS-lt15]&&echo"告警:证书即将过期">&2几个容易踩的坑
1. 泛域名证书只能走 DNS-01。*.yourdomain.com这种通配符证书,HTTP-01 签不了,必须用 DNS 验证。用 acme.sh 配 DNS API 最方便:
exportCF_Token="你的token"exportCF_Account_ID="你的account_id"acme.sh--issue--dnsdns_cf-dyourdomain.com-d'*.yourdomain.com'\--reloadcmd"systemctl reload nginx"但这里有个安全隐患:DNS API token 从此就是一份长期有效的生产凭据了,它得一直躺在这台机器上。所以务必把这个 token 的权限限制到单个 zone、只允许改 TXT 记录,绝对不要用全局 API Key。有条件的话,把签发的机器和跑服务的机器分开,token 只放在签发机上。
2. 别用certonly之后忘了 reload。前面强调过,--deploy-hook或者--reloadcmd必须配。签发成功但 Nginx 还在用旧证书,会表现成"浏览器提示证书过期,但 certbot certificates 显示还有 60 天"——这种问题挺难一眼看出来的。
3. Cloudflare 会把证书搞成两套。开了 CDN 之后,访客看到的是 Cloudflare 边缘的证书,你自己源站上的证书是另一张,两者独立。而且 Cloudflare 的 Origin CA 证书只被 Cloudflare 信任,浏览器不认,所以它不走上面这套寿命规则,别配错。
4. 验证失败先看 Nginx 的 log。ACME 报错信息很模糊(“some challenges have failed”),真正的原因在 Nginx 的 error log 里。九成情况是:location 没配对、目录权限不对、或者有个前置的重定向把/.well-known/也跳走了。
最后
整套流程就是:HTTP 先跑通 → webroot 签证书 → 配上 reload hook → 干跑验证续期 → 布一个剩余天数的告警。五分钟能配完。
但要记住 2026 年之后的一个判断:证书这件事,人工介入的余地越来越小了。90 天还能靠日历提醒手动续,45 天已经很勉强,6 天就必须全自动。
所以别把精力花在"记得续证书"上,把精力花在让续期管道绝对可靠上:reload 钩子有没有、干跑过没有、失败了有没有告警。管道可靠了,证书寿命是 90 天还是 6 天,对你来说只是配置里换个值的事。