上周三晚上十点,测试环境的接口突然全红了。

浏览器地址栏那把锁变成一道红色斜杠,控制台刷出一排 net::ERR_CERT_DATE_INVALID。我第一反应是后端挂了,登上服务器看了半天,进程活得好好的,CPU 也很闲。
那晚我走了不少弯路。
先去翻 Nginx 日志,干干净净,一条报错都没有。又怀疑是 CDN 缓存脏了,清了一遍,还是红。折腾到快一个小时,我才想起来去看一眼证书有效期。
过期了,三天前就过期了。
那一刻挺无语的。不是技术难,纯粹是运维疏忽。没人续期,没人告警,证书就那么静悄悄地到期,然后线上流量在浏览器里被直接拦下来。
先把 HTTPS 到底是什么说清楚
很多人以为 HTTPS 是个新协议,其实不是。
HTTPS 就是 HTTP over TLS,拆开看就是 HTTP 加一层 TLS 加密层。RFC 2818 在 2000 年就把它定义清楚了,默认端口 443。HTTP 那套请求响应语义一个字都没改,只是在底下垫了一条加密通道。
所以你写业务代码的时候,其实压根不用关心 HTTPS。GET、POST、Header、状态码,全是老样子。
变化只发生在这层 TLS 上。
客户端和服务端第一次见面,得先握手。客户端发 ClientHello,把支持的加密套件和自己的随机数报过去。服务端回 ServerHello,挑一套双方都认的,再把自己的证书链丢过来。客户端拿到证书,去验签名、验域名、验有效期,全部通过之后,两边各自算出一个会话密钥。
这个密钥才是后面干活的。
为什么绕这么大一圈。因为非对称加密太慢了,拿它加密整条数据流,性能根本扛不住。它只负责在握手阶段安全地交换那个对称密钥,之后的大流量传输全走对称加密。快,而且安全。
证书链那一环最容易被忽略。
你服务器上装的从来不是一张证书,通常是一串。中间证书少装一张,浏览器就可能链不完整,安卓上尤其明显,苹果设备反而没事。这种坑我踩过,同一个域名,iPhone 打得开,安卓报错,查了半天还以为是什么兼容性玄学。
证书这活儿真没必要用手扛
握手说完,回到过期那件事。
手动申请、手动上传、手动配 Nginx,然后再手动记一个到期时间,这套流程在只有一个域名的时候还行。域名一多,再加泛域名和几个 IP 证书,人脑根本记不过来。忘记续期不是意外,是必然,只分早晚。
那次事故之后,我把本地和测试环境的证书体系整个换了一遍,用的是 lcjmSSL。它背后接的是 Let's Encrypt、Google Trust Services、ZeroSSL 这些可信 CA,多域名、泛域名、IP 证书都能签。
真正省事的是它的轻量 API。
申请、验证、部署三个阶段都能自动化跑通,签发完直接把证书推到指定位置,续期也不用我盯着。从那以后我没再因为证书过期半夜爬起来。前端那边域名换了,我顺手加一条配置就行,不用像以前那样翻文档敲 certbot。
一次事故换来的教训,挺值。
现在回头看,HTTPS 本身真没什么神秘的。一层垫在 HTTP 底下的加密壳,握手换密钥,证书证明身份,剩下的交给对称加密去跑。真正磨人的一直是证书申请和续期这些基建琐事。协议是优雅的,运维是折腾的。