3.3 SSL 证书:免费 DV 证书申请与部署

域名解析通的当天下午,合伙人在微信里打开了站点,丢来一句:浏览器提示不安全,客户看到还以为我们被盗号了。我一看,http 明文裸奔,地址栏一个灰色感叹号。当晚就把证书提上日程——不是因为它难,而是它不做,前面所有工作在用户眼里都不专业。另外上了 HTTPS 才能用上 HTTP/2,对首屏速度也有帮助,这在 1.2 选型对比 里算过一笔账。

本节讲我们用免费 DV 证书搞定 HTTPS 的全过程:选证书、申请、DNS 验证、部署到 CDN、开强制跳转,外加三个差点让整站不可用的坑。腾讯云 SSL 证书服务提供免费 DV 证书,个人项目与小型创业站完全够用。

3.3.1 先选对证书类型

先给一张对照表,省得在控制台里挑花眼:

类型 验证方式 价格 签发速度 我们的项目
DV 免费版 自动 DNS 验证 免费 10 分钟内 正在用
OV 付费版 人工审核组织信息 数百元每年 数天 用不上
EV 付费版 严格人工审核 更贵 更久 用不上

DV 只验证域名所有权,不验证公司主体,但浏览器一样显示小锁,差别只是证书详情里没有公司名。对静态创业页来说,免费 DV 是唯一合理的选择。要接受的唯一代价是有效期——免费证书一次签发一年,到期必须续,这就是 3.3.4 坑二的内容。

顺带一个真实小插曲:最初我也动过自签证书的念头,本地起一个 OpenSSL 签一张,十分钟搞定。合伙人文档里查到自签证书在浏览器里照样是红色感叹号,还要手动教每个客户点继续访问,遂作罢。免费 DV 的成本是每年点几下控制台,收益是所有用户无感通过,这笔账不用算。

顺带说说为什么必须上 HTTPS,给团队算账时用得着。三条理由:一是浏览器信任,地址栏没有小锁的站点在客户眼里约等于路边摊;二是性能,HTTPS 才能协商 HTTP/2,多路复用对首屏加载有实打实的提升;三是生态强制,微信小程序的业务域名、部分平台的分享卡片预览,通通要求 https 接口,明文站点连被集成的资格都没有。成本是零,收益是三重,没有理由不做。

3.3.2 申请与 DNS 验证

控制台路径是 SSL 证书、我的证书、申请免费证书。表单只有两项要填:域名(填 www.example.com,根域需要的话再申请一张,或干脆申请泛域名证书)和联系邮箱。这里有个小技巧:证书申请的域名和 3.2 节解析的域名必须严格一致,少一个或多一个 www 都不行,浏览器校验证书时按精确匹配来。提交后进入验证:平台给一条 CNAME 记录,格式类似 _dnsauth.www 指向一串 sslverify 地址。

巧的是这条记录加在 3.2 解析 同一个解析控制台里。由于 DNSPod 与证书服务同属腾讯云生态,记录可以一键自动添加,我们手动加过一次、自动加过一次,速度没区别,自动更省事。加完回证书页点验证,几分钟内状态变绿。

一个决策点:单域名证书还是泛域名证书(星号开头的泛解析)。泛域名一张证书护住所有子域,以后加 blog、api 子域不用重新申请。我们的子域规划只有 www 和根域,所以两张单域名证书比一张泛证书更直接,也省得到期换证波及全部子域。如果产品规划里子域明确会越加越多,泛域名是更好的选择。两种方案的对比如下:

方案 覆盖范围 到期影响 我们的选择
单域名证书 一个精确域名 换证只影响该域 两张:www 与根域
泛域名证书 所有一级子域 一张过期全部子域遭殃 子域多时改用

新手最容易忽略的细节是:泛域名证书并不覆盖根域本身,星号只匹配子域层级。也就是说,买了泛证书还得再为根域单独配一张,否则 example.com 依然是明文。我们把这事写进了上线 checklist,每次新增子域前先回头看证书覆盖范围。两个选择的对照:

维度 单域名证书 泛域名证书
覆盖范围 一个精确域名 一级下所有子域
到期影响 只影响一个子域 全部子域一起换
申请成本 每个子域各一张 一张搞定
管理难度 多了容易记岔 省心

顺带提一句本地开发:https 的线上行为在本地测不了,可以用 mkcert 本地自签证书,浏览器会信任,行为和线上一致。我们做混合内容检查就是先在本地 https 环境跑通的。

3.3.3 部署:证书挂到 CDN 上

证书签发后不是自动生效的,要手动部署。部署位置取决于流量走到哪:我们的链路是 CDN 加速域名承载流量,所以选部署到 CDN:SSL 控制台里选部署、勾选 3.2 节接入的那个加速域名,一两分钟完成。如果你的站没接 CDN、直接走托管平台默认域名,就部署到托管平台;这两种位置的差别值得记住:

部署位置 适用场景 注意事项
CDN 加速域名 自定义域名走 CDN 部署完刷一次缓存
托管平台默认域 apa 域名直接用 一般送证书平台自动管理
轻量服务器 自建 Nginx 部署 证书要自己下发到服务器并配 443

部署完两件事必须配。一是强制 HTTPS 跳转,在 CDN 控制台 HTTPS 配置里打开 301 重定向,所有 http 请求自动跳 https,源站再支持明文都无所谓。二是回源协议同步改成 HTTPS,避免端到端链路里还有明文段落。

验证用无痕窗口(避开本地缓存)加命令行:

curl -I http://www.example.com
curl -I https://www.example.com

第一行返回 301 且 Location 指向 https,第二行返回 200,链路就是通的。典型输出长这样:

HTTP/1.1 301 Moved Permanently
Location: https://www.example.com/

HTTP/1.1 200 OK
strict-transport-security: max-age=31536000

浏览器里确认小锁为绿色锁定状态,证书详情能看到签发机构即可。想提前知道证书哪天到期,不用等告警,命令行直接查:

openssl s_client -connect www.example.com:443 -servername www.example.com </dev/null 2>/dev/null | openssl x509 -noout -dates

输出里的 notBefore 与 notAfter 就是起止日期,我们把它做成了每周轮询的脚本,到期前 30 天在企业微信里推送提醒。

3.3.4 三个坑

坑一:混合内容。HTTPS 跑起来,首页小锁有了,但产品详情页的小锁消失——原因是正文里两张营销图还写着 http 绝对地址。浏览器把明文图片判为不安全资源,小锁就消失。更隐蔽的是第三方统计脚本,当时我们也漏了一个,直到控制台报 mixed content 才发现。解法:全站资源统一协议,img src 一律相对路径或 https,第三方脚本实在改不了协议就换服务商。我们在 CI 里加了一条 grep 检查,产物里出现 http:// 开头的资源引用直接报错——能在构建期拦住的,绝不留到线上,这个思路在 3.4 构建排查 里展开成了完整预检清单。

坑二:证书过期。免费证书到期那周,监控突然告警整站证书失效,打开果然是浏览器拦截页。续期本身免费,但要重新走一遍申请部署流程,期间站点处于半残状态。之后设了两道保险:日历提醒提前 30 天,加云监控的证书到期告警,5.2 监控告警 那节会展开告警怎么配。另外证书的过期时间可以主动查,部署脚本里加一行自检:

openssl s_client -connect www.example.com:443 </dev/null 2>/dev/null \
  | openssl x509 -noout -dates

输出里的 notAfter 就是到期日。我们把这条命令封进 precheck,距离到期不足 21 天就在部署日志里打警告,谁部署谁看见,想忘都忘不掉。

坑三:部署后没刷新缓存。证书换了,CDN 节点上的旧会话还在,部分用户看到旧证书报错。部署完成后顺手触发一次 CDN 缓存刷新,这个动作在第四篇 4.1 CDN 接入 里被写进了部署脚本,而不是靠手动记。

小结

HTTPS 的闭环是:申请 DV、DNS 验证、部署到 CDN、开 301 强制跳转、混合内容体检、到期告警。每一步都不难,难的是把它们串成不依赖个人记忆的流程。证书挂好,第三篇只剩最后一道关卡:3.4 常见构建失败与排查,把上线前那些猝不及防的红色报错整理成清单。

延伸阅读