新闻详情

新闻详情

首页 / 资讯中心 / 详情

Ubuntu手动配置certbot泛域名证书全流程:DNS验证与Nginx部署

发布时间:2026/10/1 19:29:09来源:尧图网络
Ubuntu手动配置certbot泛域名证书全流程:DNS验证与Nginx部署
1. 为什么我建议你在 Ubuntu 上手动走一遍 certbot 泛域名证书流程直接说结论虽然大多数教程都推荐“一键签发、自动续期”但真实生产环境里真正卡人的往往不是 Let‘s Encrypt 本身而是 DNS 解析、权限路径、证书链拼接这些“看起来很简单”的细节。尤其是泛域名证书它和单域名证书的签发逻辑完全不一样手动操作一次你会把整个 HTTPS 链路彻底吃透后面排查任何 SSL 问题都比别人快一步。我这次的需求很明确一台 Ubuntu 服务器上有多个子域名服务包括 API 网关、管理后台、静态资源、内部监控等如果每个域名单独签一张证书管理成本高、续期窗口错开、Nginx 配置也臃肿。用一张*.example.com的泛域名证书能覆盖api.example.com、admin.example.com、static.example.com以后新增子服务只要 DNS 解析指向同一台机器证书直接复用省掉的麻烦不是一点半点。适合谁来参考这篇文章三类人最合适一是刚接触 Let‘s Encrypt 但被泛域名卡住的运维新手二是在 Nginx 或 Apache 上手动部署过证书、想搞清楚 certbot 底层逻辑的人三是准备把多子域名服务收敛到单一证书的团队。文章里不会有任何“复制粘贴就跑”的侥幸心态每一步我都会告诉你为什么要这么做、不做会踩什么坑。Let‘s Encrypt 的免费证书现在是互联网 HTTPS 化的绝对主力但它有一个天然限制通配符域名泛域名不支持 HTTP-01 验证只能走 DNS-01 验证。这个限制决定了你必须在 DNS 服务商那边临时添加一条 TXT 记录证书签发完成后再删掉。整个过程如果手动操作大概需要 10 到 15 分钟其中大部分时间花在等待 DNS 生效上。2. 泛域名证书的核心逻辑为什么 HTTP-01 验证在*.example.com上行不通2.1 三种验证方式泛域名只能选一种Let‘s Encrypt 的 ACME 协议支持三种域名验证方式理解它们的区别是手动配置的基础。HTTP-01 验证是默认方式CA 会访问http://你的域名/.well-known/acme-challenge/xxx这个临时路径服务器返回指定的 token 内容即可。这个方式的优点是简单但致命缺点是它只能验证具体的一个域名。你想签发*.example.com这种通配符CA 不知道该去访问哪个域名的这个路径所以泛域名验证直接不支持 HTTP-01。TLS-ALPN-01 验证是通过特定端口的 TLS 握手完成的需要在 443 端口上临时跑一个特殊配置的 HTTPS 服务同样只适用于单个具体域名。而且这个方式对服务器配置要求高很多反向代理环境下很难操作实际使用率很低。DNS-01 验证是唯一支持泛域名的方式。CA 要求你在域名的 DNS 解析里添加一条 TXT 记录记录名通常是_acme-challenge.example.com记录值是一串随机生成的验证字符串。CA 会去查询这条 TXT 记录匹配成功就能证明你对这个域名的所有权。泛域名之所以能用 DNS-01是因为你控制example.com这个域名的 DNS就意味着你有权签发该域名下的所有子域名和通配符。2.2 DNS-01 验证的完整流程从请求到签发certbot 手动模式下的 DNS-01 流程大致是这样的certbot 向 Let‘s Encrypt 的 ACME 服务器发起证书签发请求附带你的域名列表。CA 返回一个验证挑战要求你提供_acme-challenge.example.com的 TXT 记录值。certbot 会在终端显示这条 TXT 记录的内容并暂停等待。你登录 DNS 服务商的控制台在example.com的解析记录里添加一条_acme-challenge的 TXT 记录。等待 DNS 生效通常在 10 秒到 5 分钟之间然后按回车让 certbot 继续。CA 验证通过向 certbot 签发证书文件包括证书本体、私钥、中间证书链。你删除刚才添加的 TXT 记录避免留下不安全的验证入口。这套流程手动走起来的心理负担其实不大很多人是被“泛域名感觉很高级”吓住了实际上核心就一句话我得临时改一次 DNS。改完等生效证书就下来了。2.3 泛域名证书到底覆盖哪些域名边界要想清楚泛域名证书的*.example.com有几个边界规则必须提前搞清楚。第一它只匹配一级子域名api.example.com能覆盖但dev.api.example.com覆盖不了后者需要用*.api.example.com单独签一张证书。第二它不覆盖裸域名example.com本身如果你想让主站也走 HTTPS必须在签发时同时指定example.com和*.example.com两个域名Let‘s Encrypt 允许一张证书里包含多个域名。第三泛域名证书的匹配是严格的一对一不会跨域名*.example.com对*.example.org无效。我在实际项目里见过有人只签了泛域名就以为万事大吉结果裸域名example.com访问时浏览器直接报证书不匹配。所以签发命令里一定要把裸域名和通配符域名一起列入虽然这会增加 DNS 验证的 TXT 记录条数但对后续维护是值得的。3. 动手前必须准备好的三样东西域名权限、DNS 控制台和 certbot 环境3.1 域名必须是你自己能管理 DNS 的泛域名签发的前提是你对域名的 DNS 有完全控制权。这里的“控制权”具体指的是你能登录域名注册商或者 DNS 托管服务商的控制台手动添加、删除 TXT 记录。如果你用的是 Cloudflare、阿里云 DNS、DNSPod、Route 53 这类托管解析服务只要你能登录账号就满足条件。特别提醒一下如果你当前域名的 DNS 解析本身不在你需要部署证书的这台服务器上管理比如域名是在 A 服务商注册、DNS 解析托管在 B 平台那你必须在 B 平台操作 DNS 记录。这个和你服务器上装没装 DNS 服务毫无关系。另外如果 DNS 之前刚做过迁移或者换过解析商建议先确认_acme-challenge这类子域名的解析权限没有被旧服务商残留记录干扰。3.2 certbot 安装与版本确认Ubuntu 系统上安装 certbot 有两套常见方案一套是 Ubuntu 官方软件源自带的版本另一套是 certbot 官方 PPA 里的最新版。我的建议是直接使用官方 PPA因为 Let‘s Encrypt 的 ACME 协议和服务端也在迭代软件源里的 certbot 版本如果过旧可能出现兼容性问题。执行以下命令添加 PPA 并安装sudo apt update sudo apt install software-properties-common sudo add-apt-repository ppa:certbot/certbot sudo apt update sudo apt install certbot安装完成后确认版本certbot --version正常情况下你会看到类似certbot 2.x.x的输出。如果版本号很低比如0.x.x说明源有问题先解决版本再继续。这里要注意certbot 的官方文档明确推荐用 pip 安装到虚拟环境里的方式但我在生产服务器上更倾向于 PPA 方案因为后续卸载和管理更符合 Ubuntu 系统习惯apt升级也会自动处理 certbot 的依赖。对于手动配置泛域名证书这个场景PPA 版本的功能已经完全够用。3.3 DNS 服务商控制台的准备在开始签发之前先登录你的 DNS 服务商控制台找到example.com的解析记录管理页面。不同平台的界面不一样但核心操作是一致的添加记录时类型选择 TXT主机记录填_acme-challenge记录值留空等 certbot 生成后填入。有一个小技巧推荐给大家先在控制台里把页面打开到“添加记录”这一步不要关。等 certbot 跑到等待验证的环节时你只需要复制粘贴记录值、提交、等生效整体流程顺畅很多。否则等你收到验证提示再手忙脚乱地登录控制台DNS 生效时间可能已经过去一半了。4. 手动签发全流程实操从生成 CSR 到拿到证书文件4.1 Certbot 手动模式的命令解析手动签发泛域名证书的核心命令是这个sudo certbot certonly --manual --preferred-challenges dns -d example.com -d *.example.com我来逐参数解释一下这样写的原因避免你只是机械复制的certonly只获取证书不自动修改 Nginx 或 Apache 配置。这一步对手动配置场景特别重要因为很多人的服务器上可能同时跑了多个 Web 服务certbot 的自动配置插件有时会直接改配置导致服务异常。手动模式就是要文件配置自己来。--manual开启交互式手动验证模式。certbot 不会自动帮你添加 DNS 记录而是把需要添加的 TXT 记录信息显示出来等你手动操作完成后继续。--preferred-challenges dns指定使用 DNS-01 验证方式这是泛域名签发必须的参数。-d example.com -d *.example.com声明证书要覆盖的域名。这里用了两次-d参数注意*.example.com需要用单引号包裹防止 shell 把星号当做通配符展开。为什么把裸域名和泛域名都写上我在前面已经讲过了为了覆盖主站和所有一级子域名。执行这个命令后certbot 会先输出一些协议确认信息要求你同意 Let‘s Encrypt 的服务条款并且询问是否愿意将邮箱分享给 EFF 基金会。第一次使用的时候按提示输入邮箱并同意即可后续续期也会用到联系邮箱。4.2 关键环节添加 TXT 记录并等待 DNS 生效命令执行到中间环节certbot 会输出类似这样的内容Please deploy a DNS TXT record under the name: _acme-challenge.example.com with the following value: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Before continuing, verify the record is deployed.这段提示的意思非常明确去你的 DNS 控制台在example.com的解析记录里添加一条 TXT 记录主机记录填_acme-challenge记录值填输出的那一长串字符。如果你同时指定了裸域名和泛域名两个-d参数certbot 可能要求你添加两条 TXT 记录分别验证两个域名不要漏看。添加完成后不要急着按回车。先花几秒钟检查一下主机记录是否填的是_acme-challenge而不是_acme_challenge下划线和连字符的区别很要命。记录值是否完整复制有没有遗漏字符可以点开记录查看详情确认。当前生效的解析记录里是否之前已经存在同名的 TXT 记录如果有先删掉旧的避免冲突。检查无误后可以提前验证一下 DNS 是否生效这个动作能帮你省下不少等待时间。用dig命令在本地查询dig TXT _acme-challenge.example.com 8.8.8.8如果你本机没有dig工具可以用nslookupnslookup -typeTXT _acme-challenge.example.com 8.8.8.8看到返回结果中包含你刚才添加的字符串就说明 DNS 已经全球生效了。注意查询时指定8.8.8.8或114.114.114.114这样的公共 DNS 服务器避免本地缓存造成误判。确认生效后回到终端按回车certbot 会继续完成验证并签发证书。4.3 签发完成后生成的文件清单签发成功后certbot 会输出证书保存路径并提示你已经成功获取证书。CentOS 或 Ubuntu 上默认路径都是/etc/letsencrypt/live/example.com/这个目录里包含四个文件它们在 Nginx 或其他 Web 服务器配置中各自承担不同角色文件作用对应的 Nginx 配置项fullchain.pem完整的证书链包含站点证书和中间证书ssl_certificateprivkey.pem私钥文件服务器端持有ssl_certificate_keycert.pem仅站点证书不包含中间证书一般不直接用除非你需要单独合并chain.pem中间证书链拼接时使用最需要注意的是fullchain.pem和privkey.pemNginx 配置里就靠这两个文件。很多人图省事直接拿cert.pem作为证书配置结果 Android 客户端或某些浏览器会报证书链不完整原因就是缺少中间证书。fullchain.pem才是“证书 中间证书”的完整组合务必优先使用它。签完证书后还有一件重要的事没有做删除刚才添加的 TXT 记录。因为验证已经完成这条记录已经没有价值留着反而给了别人可乘之机。回到 DNS 控制台把刚才添加的 TXT 记录删除并确认生效这一步一定要做。4.4 私钥权限一定要收紧一个容易忽略的安全细节/etc/letsencrypt/live/example.com/目录里的私钥文件privkey.pem是敏感文件Nginx 的 worker 进程通常以 www-data 用户运行能够读取证书目录即可但这不代表权限可以随意放开。默认情况下certbot 会将私钥权限设置为0600属主为 root。这个权限意味着只有 root 用户能读写安全性没有问题。你在配置 Nginx 时确保 Nginx master 进程以 root 启动、reload 后能读取到私钥即可。不建议为了省事把私钥权限改成0644或者把证书文件复制到 Web 根目录下一旦私钥泄露攻击者就可以用它解密你所有用户的 HTTPS 流量后果非常严重。如果因为特殊原因必须把私钥复制到其他目录务必将权限设置为600且属主为运行 Web 服务的用户。5. Nginx 配置落地认证链完整配置和常见坑位5.1 一个可以直接套用的 Nginx 配置片段拿到证书文件后配置 Nginx 不算难但有几个细节容易翻车。先看一个完整的 HTTPS server 配置示例server { listen 443 ssl http2; server_name api.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 HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; # 其他业务配置 location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; } }这个配置里有几个关键点解释一下。ssl_protocols只开启 TLS 1.2 和 1.3TLS 1.0 和 1.1 已经进入退役阶段为了安全不要再开。ssl_ciphers选用常见的强加密套件HIGH:!aNULL:!MD5是简洁且安全的默认组合如果你没有特殊兼容性需求直接用即可。http2选项是让 Nginx 在 TLS 上启用 HTTP/2对性能有提升如果你的 Nginx 版本较低不支持去掉这个参数即可。配置完成后用以下命令测试配置文件是否有语法错误sudo nginx -t看到syntax is ok和test is successful输出后再执行 reload 让配置生效sudo systemctl reload nginx5.2 多个子域名如何共用一张证书server_name 的灵活配置泛域名证书的最大价值就是一张证书配多个子域名站。如果api.example.com、admin.example.com、static.example.com都在同一台服务器上你可以为每个服务各写一个server块每个块里的ssl_certificate和ssl_certificate_key都指向同一份文件。证书会按域名匹配浏览器验证时只要域名在证书的覆盖范围内就不会报错。如果你希望用一个server块统一处理所有泛域名下的请求可以这样配置server { listen 443 ssl http2; server_name *.example.com; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # 根据子域名分发请求 ... }不过生产环境我一般不建议这么干因为多站点架构下不同子域名的业务逻辑、静态资源路径、反向代理目标差异很大写在一个块里会让 Nginx 配置变得难以维护。按子域名拆分server块配置语义清晰后续扩展也不容易误伤其他服务。5.3 HTTP 强制跳转 HTTPS 的配置思路证书部署之后HTTP 访问应该统一跳转到 HTTPS否则用户输入http://api.example.com时浏览器不会自动升级到安全连接。在 80 端口的server块里配置一次强制跳转即可server { listen 80; server_name api.example.com; return 301 https://$host$request_uri; }注意这里用的是$host而不是$server_name这样即使用户通过子域名访问也能正确跳转到对应的 HTTPS 地址。如果你有多个子域名每个子域名都需要一个对应的 80 端口跳转块或者写一个默认的 80 端口跳转块统一处理。6. 续期实战泛域名证书的续期方式和踩坑记录6.1 为什么泛域名证书不能像单域名那样自动续期这是泛域名证书和单域名证书最大的区别也是最多人栽坑的地方。单域名证书如果走 HTTP-01 验证certbot 的自动续期任务可以在服务器上直接完成验证因为文件服务是现成的。而泛域名证书的 DNS-01 验证需要手动添加 TXT 记录certbot 自身没有 DNS 服务商的 API 权限没法替你把记录加上去所以不能完全自动化。这意味着续期时你还是得手动操作一遍 DNS添加 TXT 记录、等生效、让 certbot 验证、删记录。好消息是 Let‘s Encrypt 的证书有效期是 90 天意味着你每 90 天需要手动操作一次。听起来麻烦但实际熟练后整个过程也就是 10 分钟的事。你可以把提醒设在手机上也可以用系统日历提醒或者干脆在服务器上写个 cron 任务提前 30 天检查证书有效期如果剩余天数小于 30 天就发邮件提醒你到期前手动续签。6.2 续期命令一样的参数但要注意有效期检查证书到期前 30 天时可以执行续期命令sudo certbot certonly --manual --preferred-challenges dns -d example.com -d *.example.com --force-renewal这里多了一个--force-renewal参数作用是强制重新签发一张新证书而不是检查现有证书后跳过。如果你不加这个参数certbot 在证书还有效时会直接提示证书未到期、无需续期。执行续期时流程和首次签发完全一样。certbot 会再次生成新的 TXT 记录值。重点提醒一下DNS 控制台里旧的 TXT 记录已经删掉了吗续期时一定要确认没有同名记录残留否则新记录和旧记录并存CA 查询时会看到多个 TXT 值验证可能失败。续期成功后certbot 会更新/etc/letsencrypt/live/example.com/下的文件。Nginx 不需要重启只需要重新加载sudo systemctl reload nginx6.3 一个常见的 SSL 连接报错证书链不完整导致客户端验证失败部署泛域名证书后最常遇到的报错之一是curl: (60) SSL certificate problem: unable to get local issuer certificate这个错误的核心原因是服务器返回的证书链不完整客户端无法验证证书的信任链。大多数时候是因为你把cert.pem当成了fullchain.pem来配置。注意我前面那张表格里的说明cert.pem只有站点证书不包含中间证书所以客户端中间证书缺失就断了验证路径。解决方法是把 Nginx 配置里的ssl_certificate改成/etc/letsencrypt/live/example.com/fullchain.pem。还有一种情况是配置没问题但客户端本地 CA 证书库过旧不认识 Let‘s Encrypt 的中间证书。这种情况一般不会出现在主流浏览器上但会出现在一些老系统或定制系统的curl命令上。解决办法是更新客户端的 CA 证书库Ubuntu 上用sudo apt install ca-certificates6.4 端口和防火墙问题证书配置正确但外网访问失败证书签发成功、Nginx 配置无误但外网访问https://api.example.com超时或者拒绝连接这时候十有八九是防火墙没放行 443 端口。Ubuntu 默认的ufw如果之前没有放行该端口会拦截所有 HTTPS 请求。执行sudo ufw allow 443/tcp sudo ufw status如果你用的是云服务器还需要去云控制台的安全组或防火墙规则里放行 443 端口。云服务器上配了 ufw 但没同步安全组规则的情况非常常见排查时要两头一起看。7. 常见问题速查表一次性解决你绕不开的 SSL 报错报错或现象根本原因解决方案SSL certificate problem: unable to get local issuer certificateNginx 配置使用了cert.pem而不是fullchain.pem修改ssl_certificate为fullchain.pemreload Nginxcurl: (60) SSL certificate problem客户端 CA 证书库过旧更新客户端ca-certificates包NET::ERR_CERT_COMMON_NAME_INVALID访问域名不在证书覆盖范围内比如dev.api.example.com超出了*.example.com的匹配范围重新签发包含该域名的证书或确保访问域名是一级子域名SSL connection required but not provided服务端未启用 TLS数据库客户端要求 SSL 连接检查服务端配置是否开启 SSL 支持DNS 验证失败提示 TXT 记录未找到添加 TXT 记录有误或 DNS 尚未生效检查主机记录是否为_acme-challenge使用dig查询公共 DNS 确认生效nginx: [emerg] cannot load certificate key私钥权限过大或属主不对Nginx 无法读取设置privkey.pem权限为600确保属主为 root 或 Nginx 运行用户访问 HTTPS 超时防火墙或云安全组未放行 443 端口ufw allow 443/tcp并确认云控制台安全组规则8. 手动配置完成后我建议你做的一件事写一个续期脚本提示自己说实话泛域名证书手动配置唯一的痛点是续期。我踩过两次证书到期才发现没续的坑之后在服务器上写了一个简单的 Shell 脚本用来检查证书剩余有效期不足 30 天就输出警告并在 Nginx 访问日志里记录。你完全可以照这个思路自己做#!/bin/bash expire_date$(openssl x509 -enddate -noout -in /etc/letsencrypt/live/example.com/cert.pem | cut -d -f2) expire_epoch$(date -d $expire_date %s) now_epoch$(date %s) days_left$(( (expire_epoch - now_epoch) / 86400 )) echo Certificate days left: $days_left if [ $days_left -lt 30 ]; then echo Warning: certificate will expire in $days_left days fi把脚本放到/usr/local/bin/check_ssl_expiry.sh配合 cron 定期执行0 9 * * * /usr/local/bin/check_ssl_expiry.sh /var/log/ssl_check.log 21这样做的好处是每次登录服务器时可以先看一眼有没有警告提前安排好续期时间不用每次都等到浏览器报“您的连接不是私密连接”才开始手忙脚乱。我个人习惯是每次续期完顺手把下一次的续期提醒写到手机日历里双重保险。以我自己的体会来说手动配置泛域名证书这件事难度不在命令本身而在于对 DNS 验证流程的理解和对证书文件链路的把握。只要把 DNS-01 验证的原理想明白后面所有的问题排查都只是顺藤摸瓜的事。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

森利威尔SL4115宽压LED恒流驱动芯片设计与选型指南 2026/10/1 22:55:36

森利威尔SL4115宽压LED恒流驱动芯片设计与选型指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
自适应遗传算法在配电网DG选址定容中的Matlab复现与调试 2026/10/1 22:55:29

自适应遗传算法在配电网DG选址定容中的Matlab复现与调试

第一次拿到这个课题,我的第一反应是:这不就是把遗传算法套到配电网DG选址定容上,换个自适应算子,然后在IEEE33和IEEE118上各跑一遍的事儿吗?等我真正动手复现才发现,细节远比想象中多。今天这篇就把整个复现…

阅读更多 →
智能表单实战:Schema 建模、条件显隐、校验时机与自动填充 2026/10/1 22:55:29

智能表单实战:Schema 建模、条件显隐、校验时机与自动填充

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
PVE服务器硬件监控:CPU温度、硬盘温度与UPS状态集成指南 2026/10/1 22:55:28

PVE服务器硬件监控:CPU温度、硬盘温度与UPS状态集成指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
DevHub深度拆解:AI一键生成应用与成本账本的设计之道 2026/10/1 22:55:21

DevHub深度拆解:AI一键生成应用与成本账本的设计之道

写这篇稿子之前,我先说明一下背景。最近我在产品的选型阶段,密集接触了一批"AI一键生成应用"的工具,其中一个让我印象特别深的就是 DevHub。它的起点其实很简单——你在输入框里用自然语言描述一个想要的东西,比如"…

阅读更多 →
TestableMock自定义Mock处理器:从字节码增强到测试替身实战 2026/10/1 22:55:21

TestableMock自定义Mock处理器:从字节码增强到测试替身实战

我刚开始用TestableMock那阵子,一直把它当Mockito的“平替”来用——写个MockMethod,把外部依赖替换掉,省去一堆when().thenReturn()的样板代码。直到有个老项目里的一个静态工具类怎么都Mock不生效,我才开始认真翻它的源码&#…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉