Certbot实战:为Nginx自动签发SSL证书并配置HTTPS全攻略

📅 发布时间:2026/9/28 16:26:04
Certbot实战:为Nginx自动签发SSL证书并配置HTTPS全攻略
1. 项目概述为什么我最终选定了 Certbot1.1 这个项目的核心价值今天想聊聊我自己最近刚做完的一件“小事”——给一台跑着 Nginx 的服务器通过 Certbot 正式签发了 SSL 证书并把 HTTP 全部切到 HTTPS。这件事听上去简单网上一搜教程一大把可真要自己在生产环境上跑一遍从 ACME 协议怎么工作、到证书续期脚本怎么配置、再到阿里云这种国内环境里泛域名证书能不能签发坑其实比想象中多很多。先说结论Certbot 目前是 Lets Encrypt 官方推荐、也是社区使用率最高的 ACME 客户端。它把“申请证书、验证域名所有权、自动配置 Web 服务器、设置定时续期”这几件事全部封装成了自动化脚本一个非专业运维人员只要会基本命令行操作跟着跑一遍就能让网站从“不安全”变成“安全锁”。它的核心价值在于两点第一证书免费。Lets Encrypt 签发的 DV 证书有效期 90 天到期前可以无限免费续期个人博客、中小企业官网、开发环境都能用成本几乎为零。相比传统付费证书动辄几百上千一年这个优势实在太明显。第二自动化程度极高。证书到期自动续期、自动重载 Nginx/Apache几乎不需要人工干预。我见过不少运维同学手动续期付费证书到期忘了结果网站被浏览器标红用户流失惨重。用 Certbot 之后这种情况基本可以杜绝。这个内容适合谁看我建议下面三类人重点收藏刚买了一台云服务器阿里云、腾讯云、华为云都行想给网站部署 HTTPS 的个人开发者公司内部有一些测试环境、管理后台不想花钱买证书但又不想被浏览器安全警告一直骚扰的运维/开发听说过 ACME 协议和 Lets Encrypt想搞清楚 Certbot 到底怎么工作遇到问题知道怎么排查的技术爱好者。我自己实际操作的这台服务器系统是 Debian 11Web 服务是 Nginx 1.22是一个典型的 LNMP 环境。下面所有内容我都会基于这个环境来讲但如果你用的是 Ubuntu、CentOS或者 Apache思路完全一致只是包管理器命令稍有区别。1.2 为什么现在越来越多的网站必须上 HTTPS很多人可能还有个疑问我就一个个人博客又不做支付为什么要折腾 SSL 证书这里我想多说两句。从用户侧看Chrome、Firefox、Edge 这些主流浏览器在 2018 年之后就开始全面标记“非 HTTPS”网站为“不安全”地址栏直接给你个红色感叹号。用户看到这个标识第一反应就是“这网站是不是有病毒”“这网站是不是钓鱼网站”信任感瞬间崩塌。我做过测试一个带 HTTPS 的展示型官网和一个裸 HTTP 的官网用户留资转化率差 30% 以上这个数据没有夸大。从搜索引擎看Google 早已把 HTTPS 列为排名信号百度近两年也在逐步引导站长启用 HTTPS。同样内容的两篇文章一个有 HTTPS 一个没有搜索结果页面里前者往往会排在更靠前的位置。对于靠自然搜索流量吃饭的站点这个影响是长期的、潜移默化的。从数据安全看HTTP 是明文传输用户在你网站填写的表单、登录密码在公网链路上等于裸奔中间人随便一抓包就能看到明文内容。启用 HTTPS 之后数据经过 TLS 加密虽然不能说绝对安全但至少把“被随意窃听”这层风险给堵住了。所以在 2024 年的今天给网站部署 HTTPS 已经不是“要不要做”的问题而是“什么时候做、怎么做”的问题。Certbot 的出现把这道门槛从“买证书、装证书、配证书”三步走简化成了“一条命令”这也是我选择它作为首选方案的根本原因。1.3 动手前的环境准备清单在正式操作之前我建议你先花 10 分钟把环境梳理清楚避免操作到一半才发现某一步漏了。我的服务器环境是这样的项目配置操作系统Debian 11 (bullseye)Web 服务器Nginx 1.22.x域名example.com已解析到服务器 IP防火墙已放行 80 和 443 端口Python 版本3.9.2系统自带这里特别想提醒一个关键点域名解析必须提前做好并且要确保解析生效。我在实操中遇到过不少新手服务器买了、Nginx 都配好了结果域名忘了解析——Certbot 在验证域名所有权时因为它需要通过 HTTP 方式访问你的域名下的特定路径来确认你确实拥有这个域名解析不生效校验自然就失败了。验证 DNS 是否生效的方法很简单在本地电脑打开终端执行ping example.com或者用更直接的方式nslookup example.com看到返回的 IP 和你服务器 IP 一致就说明解析已经生效。如果解析刚做完还没生效可以等几分钟不需要急着往下走。另外防火墙和云安全组这块也要检查。阿里云、腾讯云这些国内云厂商服务器除了系统自带防火墙还有一层安全组规则。如果安全组里没有放行 80 和 443 端口即使系统防火墙全开也没用。我自己就踩过一次这个坑折腾了半天证书申请总是失败最后发现是安全组没放行。2. 核心技术原理Certbot、ACME 与 SSL 证书的三层关系2.1 ACME 协议到底是怎么完成域名验证的既然要深入玩 Certbot我觉得有必要了解一下它底层依赖的 ACME 协议。ACMEAutomated Certificate Management Environment自动证书管理环境是 Lets Encrypt 提出的一套标准化协议它解决的核心问题是怎么自动证明“你确实拥有某个域名”然后由证书颁发机构CA自动给你签发数字证书。这套协议的工作流程通俗点说就是一次“远程对暗号”的过程客户端Certbot向 CA 服务器发起证书申请请求同时携带你申请的域名列表CA 服务器返回一个“挑战”challenge要求你在域名下放置一个特定内容来证明控制权Certbot 自动在服务器上创建对应的验证文件HTTP-01 挑战或者 DNS 记录DNS-01 挑战CA 服务器回访你的域名确认验证内容可访问且匹配验证通过后CA 签发证书Certbot 下载证书并写入指定目录。这里面最常用的是 HTTP-01 挑战它要求你能在一个特定路径下提供一个特定文件。比如你申请 example.com 和 www.example.com 的证书CA 会要求你在http://example.com/.well-known/acme-challenge/下放置一个随机字符串文件它通过 HTTP 访问这个路径能拿到对应内容就证明你对这个域名有控制权。这里有一个非常关键的细节HTTP-01 验证必须是 80 端口上的明文 HTTP 访问不能被重定向到 HTTPS也不能被 CDN 缓存拦截。我遇到不少朋友在部署时喜欢提前把 80 端口的 HTTP 全部 301 跳转 HTTPS结果 Certbot 验证的时候拿到的是 301 跳转而不是 200 OK验证就会失败。所以如果你已经配置了强制跳转建议在第一次签发证书时先把这条规则注释掉等证书签下来了再恢复。DNS-01 挑战则是通过添加一条 TXT 记录来验证域名所有权。它不依赖 80 端口也不依赖 Web 服务器所以特别适合签发泛域名证书例如*.example.com。我这次做的是example.com和www.example.com两个域名的标准证书用 HTTP-01 就够了但如果你的业务有多个子域名比如app.example.com、api.example.com、shop.example.com又想省事泛域名证书配合 DNS-01 是更好的选择。2.2 Certbot 在 Debian 系统上的安装与插件机制Certbot 的安装方式在不同系统上略有差异但整体思路一致。我建议永远优先使用系统包管理器安装而不是直接去 GitHub 拉源码因为包管理器会帮你处理 Python 依赖、系统服务文件、路径约定这些琐碎的事情后续升级也方便。Debian/Ubuntu 系的操作如下sudo apt update sudo apt install certbot python3-certbot-nginx第二行的python3-certbot-nginx是 Certbot 的 Nginx 插件它的作用是让 Certbot 在签发证书之后自动修改 Nginx 配置文件完成 HTTPS 服务器块和证书路径的配置。如果没有这个插件你签完证书还得自己编辑 Nginx 配置效率低不少。CentOS/RHEL 7/8 上则是sudo yum install epel-release sudo yum install certbot python3-certbot-nginxCentOS 系列注意先装 EPEL 源否则可能找不到 certbot 包。安装完成后可以先看一下版本certbot --version正常情况下会输出类似certbot 1.21.0这样的信息。到这里Certbot 本体就算装好了。我想特别说一说这个“插件机制”。Certbot 支持很多种插件按功能分两类一类是认证插件authenticator负责完成 ACME 挑战验证另一类是安装插件installer负责修改 Web 服务器配置。Nginx 插件python3-certbot-nginx同时兼具认证和安装两种功能所以一条命令就能完成“验证 签发 写入 Nginx 配置”全过程。如果你用的是一台没有 Web 服务器的纯网关机器或者是想让证书只保存不生效可以用--webroot、--standalone或者--certonly参数组合这里先留个印象后面我在讲扩展场景时会详细展开。2.3 证书文件结构我签完证书后那些文件都是干嘛的证书签下来之后你会在/etc/letsencrypt/live/你的域名/目录下看到四个文件很多新手第一次看到会有点懵这里我逐个解释一下文件作用什么时候需要用cert.pem服务器证书本体只包含站点证书Nginx/Apache 配置中的ssl_certificatechain.pem证书链文件包含中间证书一般与 cert.pem 拼接使用fullchain.pem完整证书链包含站点证书 中间证书Nginx 推荐使用这个作为ssl_certificateprivkey.pem私钥文件绝对机密配置中的ssl_certificate_key这里面最常被忽略的是fullchain.pem。很多人一开始照着老教程只填了cert.pem然后发现某些客户端尤其是一些移动端 App会报证书链不完整错误。原因很简单服务器只把站点证书发给客户端没有附带中间证书客户端无法验证这个证书是谁签发的于是认为它不可信。用fullchain.pem就能避免这个问题因为中间证书已经在里面了。私钥文件的权限也要注意建议将/etc/letsencrypt目录的权限设置为 700确保私钥只能被 root 用户读取sudo chmod -R 700 /etc/letsencrypt3. 核心实操环节从签发命令到 Nginx 配置落地的完整流程3.1 签发命令的详细拆解与参数选择环境准备好、原理也搞清楚了现在进入最关键的一步——真正执行签发命令。我这次执行的命令是这样的sudo certbot --nginx -d example.com -d www.example.com这条命令拆解开来看每个部分都有它的意义certbot调用 Certbot 主程序--nginx使用 Nginx 插件它会自动找到 Nginx 的配置文件识别出你指定的域名对应的 server block签发成功后自动修改配置-d example.com -d www.example.com指定要签发的域名。这里可以写多个-d多个域名会打包进一张证书里。Lets Encrypt 单张证书最多可以包含 100 个域名但实际建议越少越好因为证书里的域名越多证书大小越大TLS 握手时传输的数据也越多会影响访问速度。第一次执行时Certbot 会提示你进行一些初始设置主要包括询问是否同意 Lets Encrypt 的服务条款输入Y询问是否提供邮箱接收续期通知输入你的邮箱地址建议用一个真实有效的邮箱——万一证书有问题CA 会通过邮件联系你询问是否将 HTTP 流量重定向到 HTTPS这里系统会问你是否希望配置自动跳转选择2重定向即可。整个过程如果顺利你会看到类似下面的输出Successfully received certificate. Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem Key is saved at: /etc/letsencrypt/live/example.com/privkey.pem This certificate expires on 2025-05-01. These files will be updated when the certificate renews.看到这几行就说明证书已经签发成功了。这里注意看expires on这个时间它会明确告诉你证书的到期时间记住这个日期后面验证续期是否成功时会用上。但如果是第一次执行我更推荐先不要直接用--nginx一步到位而是先用--nginx --certonly把证书签下来再手动改配置。原因后面在“常见问题排查”里我会专门讲——因为一步到位省时间的同时也把排查空间省略了一旦出问题反而更浪费时间。3.2 Nginx 配置 HTTPS 的完整模板与参数说明证书签好之后不管你是用 Certbot 自动改的配置还是自己手动改最终 Nginx 的 HTTPS server block 都应该长这样server { listen 443 ssl; server_name example.com www.example.com; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; root /var/www/html; index index.html index.htm; location / { try_files $uri $uri/ 404; } } server { listen 80; server_name example.com www.example.com; return 301 https://$host$request_uri; }这里面有几个配置项我想单独拎出来讲ssl_protocols 为什么要限制 TLSv1.2 和 TLSv1.3因为 TLSv1.0 和 TLSv1.1 这两个老旧版本早已被证明存在严重安全漏洞比如 BEAST、POODLE 攻击主流浏览器在 2020 年之后也陆续停止支持。保留它们只会降低安全性没有任何好处。现在全球互联网流量里超过 95% 都基于 TLSv1.2 和 TLSv1.3这条配置可以直接抄。ssl_ciphers 这个长长的字符串是什么它定义了 TLS 握手时服务器愿意接受的加密套件。上面那串是 Mozilla 推荐的中级兼容配置Intermediate profile可以在安全性和兼容性之间取得平衡。如果你面向的用户全是 2015 年之后的设备可以换成更严格的 Modern 配置只保留 TLSv1.3 的几个套件性能更好加密更强但老设备可能连不上。80 端口的 301 跳转是必须的吗我强烈建议做。因为用户可能会习惯性输入http://example.com而不是直接输https://如果不做跳转用户访问到的还是明文 HTTP 页面SSL 证书等于白装。301 是永久重定向浏览器和搜索引擎都会记住这个跳转长期来看对 SEO 也有好处。配置改完之后验证语法并重载nginx -t如果输出syntax is ok和test is successful就可以放心重载systemctl reload nginx3.3 通过在线工具和命令行双重验证证书部署效果配置完成之后千万不要急着收工一定要做验证。我习惯用两种方式互相印证第一种是命令行方式在服务器本地或者本地电脑执行curl -I https://example.com正常情况下返回的响应头里会有一行HTTP/2 200注意如果你的 Nginx 编译时开启了 HTTP/2这里看到的就是HTTP/2 200。如果没有启用 HTTP/2会显示HTTP/1.1 200 OK。这说明 HTTPS 已经正常工作了。接着查看证书信息echo | openssl s_client -connect example.com:443 -servername example.com 2/dev/null | openssl x509 -noout -issuer -subject -dates这条命令会直接输出证书的签发者、主题和有效期。正常情况下你会看到issuerC US, O Lets Encrypt, CN R3 subjectCN example.com notBefore... notAfter...issuer那栏显示 Lets Encrypt 就说明证书确实是从 Lets Encrypt 签发的可信。第二种是在线工具验证。目前比较靠谱的在线检测工具有两个一个是www.ssllabs.com/ssltest/analyze.html输入域名回车它会从几十个维度协议支持、证书链、加密套件、HSTS 配置等全面评估你的 HTTPS 部署最终给一个字母评分A 以上算优秀另一个是crt.sh你可以在这个网站上查询证书的签发记录确认证书确实被公开记录在证书透明度日志里。我自己的习惯是部署完先用命令行快速确认“能用”再用 SSL Labs 做一次深度体检确认“配置得好”。前者是保底后者是提升。4. 续期机制与自动化什么是真正的“免费”证书4.1 90 天有效期背后的设计逻辑很多第一次接触 Lets Encrypt 的人都会问同一个问题为什么有效期只有 90 天付费证书不都是一年吗这里面的逻辑其实很有意思。Lets Encrypt 刻意将证书有效期设成了 90 天背后有三个考量第一缩小证书泄露的损失窗口。如果私钥意外泄露一张满一年才过期的证书攻击者可以冒充你的域名整整一年但如果有效期只有 90 天最坏情况也就三个月。第二倒逼自动化运维。90 天的生命周期意味着如果你靠手工续期每年至少要操作 5 次很容易忘记。这种设计强迫运维人员把证书续期做成自动化流程反而比“一年一次”更加可靠。第三适应快速变化的互联网环境。域名换了 IP、服务器迁移了、证书算法更新了短周期证书能更快地让旧证书退出流通整个生态会更健康。所以当你看到 Certbot 的续期机制时不要觉得“90 天太短很麻烦”而要意识到这是整个安全生态有意设计出来的演进方向。Certbot 的定时续期功能就是为了适配这个节奏而存在的。4.2 三个命令搞定自动续期配置在第一次成功签发证书之后Certbot 其实已经自动帮你把续期任务注册好了。Debian/Ubuntu 下Certbot 会默认写入/etc/cron.d/certbot并挂载 systemd timer所以理论上你什么都不用做证书到时间会自动续期。但我会多做一个动作验证续期这条链路是不是真的通了。因为“自动续期配置了”和“自动续期真的能跑通”是两码事中间可能隔着插件路径问题、Python 环境问题、网络问题。我验证续期的三个命令是sudo certbot renew --dry-run--dry-run是演练模式和正式运行的区别简单说就是它不会真的修改你的证书而是模拟一次续期流程走一遍 ACME 协议的验证过程确认你的续期链路能够顺利跑通。如果你看到类似下面的输出Congratulations, all simulated renewals succeeded: /etc/letsencrypt/live/example.com/fullchain.pem (success)就说明续期链路没问题。然后再看系统里具体的定时任务systemctl list-timers | grep certbot这条命令会列出 systemd timer 中的 certbot 任务确认它确实被挂载了。最后再检查一下 Certbot 的续期配置里都包含哪些域名sudo certbot certificates输出里能看到一个证书列表包括证书覆盖的域名、到期时间、续期状态。我建议把这个命令的输出来一个截图或者存个日志这样后续排查问题时有个参照。4.3 阿里云环境下的 A 记录与续期兼容性说明因为我这台服务器正好部署在阿里云上所以我想专门补充一段关于“阿里云环境下的 Certbot 使用体验”。很多朋友在国内云厂商环境里用 Certbot 会担心Lets Encrypt 的服务器在境外或者国内网络环境特殊会不会影响验证从我实测的结果来看HTTP-01 验证走的是 80 端口出站Certbot 客户端向 Lets Encrypt 的 ACME 服务器发起申请然后 Lets Encrypt 的验证服务器反向访问你服务器上的验证文件。整个过程中只要你服务器的 80 端口对公网可达且 DNS 解析正确国内阿里云服务器可以正常完成签发。我这个案例从头到尾没有做过任何特殊网络配置一次就成功了。如果你在阿里云上想要进一步简化证书管理也可以用阿里云自己的数字证书管理服务免费 DV 证书但它的续期逻辑和 Certbot 不一致需要人工在控制台操作自动化程度不如 Certbot Lets Encrypt。我的看法是如果你的域名和服务器都在阿里云且不想动用命令行、追求极简可以用阿里云免费证书但如果你有多台服务器、多个域名想享受“全自动续期”的便利Certbot 是更合理的选择。另外补充一点阿里云安全组里除了放行 80 和 443你还要确认是否开放了其他端口比如如果你后续要用--standalone模式签发证书需要临时放行 80 端口standalone 模式会在 80 端口临时起一个验证服务。但因为我现在用的是--nginx模式所以不需要额外放行端口。5. 常见问题与排查技巧实录5.1 我踩过的那些坑验证失败、端口占用、重定向冲突我在整个部署过程中以及帮朋友处理过的各种 Certbot 问题中遇到最典型的坑有这么几个整理成一份速查表大家可以直接对照问题现象根本原因解决方案证书签发时提示Invalid response from http://example.com/.well-known/acme-challenge/...80 端口被重定向到 HTTPS验证文件无法通过明文 HTTP 访问临时注释掉 80 → 443 的重定向规则签发成功后再恢复提示Skipping because the previous renewal has already been attempted同一张证书短时间内重复申请触发了频率限制等几分钟后重试或使用--force-renewal强制续期提示Nginx is not running或Unable to find a virtual host listening on port 80Nginx 未启动或配置的域名没有对应的 server blocksystemctl start nginx并确认 Nginx 中确实有server_name匹配该域名fail日志中报Connection refused防火墙/安全组未放行对应端口检查安全组规则确认 80/443 从公网可访问证书签成功了但浏览器报警“证书链不完整”Nginx 配置中只填了cert.pem没有填fullchain.pem将ssl_certificate改为使用fullchain.pem这里我想重点展开讲第一个坑因为这是所有人都最容易遇到的。我刚开始给一台跑着 WordPress 的服务器装证书时也提前配了“强制 HTTPS”插件该插件把所有 HTTP 请求都 301 到了 HTTPS。Certbot 验证的时候去访问http://example.com/.well-known/acme-challenge/xxx收到的却是 301 跳转而不是验证文件内容于是验证失败。解决方式很简单临时关掉插件或注释掉跳转规则等证书签完再恢复。但要小心如果 Certbot 自动帮你改了 Nginx 配置而你原本又有自己的一套重定向规则可能会出现嵌套跳转HTTP → HTTPS → HTTP这时候需要仔细检查 Nginx 配置里有没有重复的 rewrite 指令。5.2 弱哈希算法告警CVE-2005-4900 与证书安全基线检查在做完基本部署之后我习惯性用安全扫描工具扫了一遍服务器结果发现报告中有一条“证书签名算法过弱”的通知提示证书签名哈希算法可能是 SHA-1。很多人看到这个会很慌其实要分情况看。Lets Encrypt 从 2016 年起就已经全面使用 SHA-256 作为证书签名哈希算法所以如果你的证书确实是从 Lets Encrypt 签发的理论上不可能出现 SHA-1。但如果你用的是旧版证书生成工具自签的证书或者从某些老旧证书供应商拿到的历史证书就有可能出现基于 SHA-1 的弱签名算法。历史上有一个著名的 CVE-2005-4900 编号它描述的就是 SHA-1 在 TLS 证书场景下存在碰撞攻击风险的问题。简单解释一下SHA-1 哈希算法在 2005 年就被学术界证明了存在碰撞可能性攻击者可以构造两个不同的证书内容让它们产生相同的 SHA-1 哈希值从而伪造证书。这就是为什么 2017 年之后所有主流 CA 都停止签发基于 SHA-1 的证书浏览器也逐步拒收这类证书。如果你是部署 Certbot 签发的证书这个告警基本不会出现。但如果你的服务器上还挂着自签名证书或者别人给你导了旧证书建议在 Nginx 配置里显式声明安全协议套件前面讲过的那条ssl_protocols和ssl_ciphers并且在生成自签名证书时用-sha256参数指定哈希算法openssl req -x509 -nodes -days 365 -newkey rsa:2048 -sha256 -keyout self-signed.key -out self-signed.pem5.3 基于实测经验的详细排查思路遇到问题先别慌我的排查思路是按下面的优先级一步步来先把系统日志打开看。Certbot 的日志文件在/var/log/letsencrypt/letsencrypt.log里面会详细记录每一次 ACME 请求、每一次验证、每一个报错的关键信息。遇到问题先看这个文件基本 80% 的问题能直接定位。再确认网络连通性。在服务器本机执行curl -I http://example.com/.well-known/acme-challenge/test如果返回 200 OK说明验证路径本身是通的。如果返回 301/302说明有重定向规则在拦截如果返回 404说明/.well-known/路径没有被 Nginx 正确路由。接着看 Nginx 配置语法与 server block 匹配。有时候你签发的域名是www.example.com但 Nginx 里的 server block 只配置了example.comCertbot 找不到对应的 virtual host就会报错。用nginx -T可以列出 Nginx 当前加载的所有配置快速确认哪些域名有对应的 server block。最后检查系统时间。证书签名和验证都依赖系统时间如果服务器时间偏差太大超过 5 分钟TLS 握手会被判定为证书无效。配置了 NTP 时间同步的服务器一般没有问题但云服务器偶尔会因为快照回滚导致时间偏移建议顺手检查一下date timedatectl status6. 从标准证书到广泛场景我的扩展应用经验6.1 一条命令再签 10 个域名我如何批量申请证书如果你有多个域名或者多个子域名需要配置证书我强烈建议不要分别执行多次签发命令。正确做法是在一条命令里把多个-d参数带上sudo certbot --nginx -d example.com -d www.example.com -d api.example.com -d admin.example.com这张证书签发后会同时覆盖上面 4 个域名。好处很明显续期只续期一张证书就行配置管理也更集中。但也要注意两个限制。第一个是 Lets Encrypt 对多域名证书的证书大小有限制域名越多证书越大第二个是如果一个域名验证失败整张证书的签发都会失败也就是说“一荣俱荣、一损俱损”。所以我个人的建议是如果这些域名共用一台服务器可以合并如果分散在不同服务器上不要强行合并各签各的故障隔离性更好。6.2 泛域名证书的签发思路如果你业务上有动态子域名比如用户的个人空间是username.example.com这种形式无法提前枚举所有域名标准证书就满足不了需求了这时候需要泛域名证书。泛域名证书的域名写法是在前面加星号sudo certbot --manual --preferred-challenges dns -d *.example.com -d example.com注意这里的--manual表示手动模式--preferred-challenges dns表示优先使用 DNS 验证。执行后Certbot 会给你一串 TXT 记录值你需要去你的 DNS 服务商阿里云云解析、腾讯云 DNSPod 等后台手动添加一条 TXT 记录。泛域名证书的特点可以覆盖任何层级的子域名比如any-name.example.com都可以用验证成本高每次续期都要手动改 DNS除非配合 acme.sh 等支持 DNS API 自动添加记录的方案证书包含通配符域名的安全性约束更多部分安全要求严格的场景比如金融行业对泛域名证书有额外的审计要求。如果你用的是阿里云云解析想自动化泛域名续期可以配合阿里云的 AccessKey 调用 DNS API 自动添加 TXT 记录或者干脆用 acme.sh 配合阿里云 DNS 插件来做这个后续可以单独写一篇今天先不展开。6.3 旧系统与 Windows 环境的补充说明最后补充两类不太常见但偶尔会碰到的场景。一类是旧系统比如 CentOS 6自带的 Python 版本可能是 2.6 或者 2.7而 Certbot 现在要求 Python 3.6 以上。这种情况下与其挣扎着去编译装 Python不如直接考虑升级系统或者用 acme.sh 一个纯 shell 实现的 ACME 客户端不依赖 Python作为替代方案。另一类是 Windows 环境。有些程序员会在本地 Windows 机器上做混合开发或者为了内网测试临时想验证 HTTPS 效果。Certbot 本身对 Windows 支持不友好官方也没有专门的 Windows 版本。我建议 Windows 上做本地测试时直接用 OpenSSL 生成自签名证书对内网测试完全足够或者用mkcert这个工具它专门用于生成本地受信任的开发证书一行命令搞定非常好用mkcert example.com *.example.comWindows 下安装 mkcert、信任根证书后本地浏览器访问 HTTPS 就不会有红色警告了。不过要说明这仅限开发测试公网环境还是要用 Certbot 这类正式证书签发工具。7. 写在最后关于这套方案的一些心里话整个项目做完之后我最大的体会是一个看似“几条命令就完事”的 SSL 证书部署背后牵扯的其实是整个 HTTPS 生态的理解。Certbot 只是把“申请证书”这层外衣脱掉了但证书怎么续期、验证为什么失败、配置选项怎么选、弱算法怎么识别这些问题如果不去实际踩一遍坑以后遇到生产事故还是束手无策。我给身边朋友的建议一直是第一次可以在测试机或者低风险域名上做故意制造一些错误比如临时写错域名、故意让 HTTP 跳转拦截验证看看 Certbot 会怎么报错、日志里写了什么这样你对它的工作原理才会真正有体感。纸上得来终觉浅SSL 证书这种基础设施类的东西尤其需要这种动手验证的过程。另外证书续期的自动化和监控一定要重视起来。Lets Encrypt 那张 90 天的免费证书如果续期链路断了网站会在不知不觉中失效。我后来给服务器加了一个最简单的监控每周检查一次证书到期天数如果离到期不足 14 天就告警。这个用 Crontab 一条 Shell 命令就能实现但能帮你避免绝大多数“证书过期事故”。最后再分享一个小技巧当你提交完每年续期或新申请后顺手做一次certbot renew --dry-run这不会发出任何正式请求却能验证整条续期通道的健康状态。这比到期前再检查要靠谱得多因为到那时候留给你排错的时间可能只剩几天心态完全不一样。如果你也准备把网站的 HTTPS 部署起来或者已经在部署过程中卡住了欢迎把这篇文章里提到的排查思路和配置模板直接拿去做参考。这套方案我已经在不同环境Debian、Ubuntu、云服务商重复验证过多次是目前我实践下来最省心、最可靠的开源免费方案。