根域名和 www:一张证书,两条都能走的入口

有人记住的是不带 www 的地址,有人从旧链接点进来时带着 www。如果只给其中一个名字准备证书,另一个就会在浏览器里停在安全警告上。两条入口都要能打开,并不等于必须把其中一个永远改写成另一个。读者并不欠你一个“正确的主机名”。他们使用的那个名字,只要解析到你的机器,就应该得到同一份可以信任的页面。

旧书签、搜索结果、纸质名片、邮件签名和别人文章里的链接,都不会在你改版的那天一起更新。有人十年前养成了先敲 www 的习惯,有人从地址栏的建议里点到不带 www 的那一行。你无法在门口清点他们。能做的是让这两个名字都指向你准备好的入口,并且在加密握手时都拿得出身份。

下文用 example.com 表示根域名,用 www.example.com 表示带 www 的名字,用 old.example.org 表示同一台机器上已经在运行、且与这次无关的旧站点。这些名字是占位。换成你自己的名字时,关系保持不变:两个新名字是一对,旧名字是对照,不要把对照也卷进新证书。

本篇目标: 用一张证书同时覆盖根域名和 www,让 HTTP 跳到各自的 HTTPS,同时不打断机器上已有的网站。

两个名字,一条不打扰别人的验证
两个名字,一条不打扰别人的验证放大图解 ↗

先分清解析、证书和页面

这三件事经常被说成“域名配好了”。这句话把三个问题叠成一句,于是任何一层失败,听起来都像整件事失败。分开之后,你才知道该改记录、改证书,还是改站点规则。

解析回答的是:这个名字指向哪台机器。根域名和 www 可以指向同一处,也可以暂时指向不同的地方。你要的是两者都指向准备提供页面的那一台,而且从公网看过去确实如此。只在自己的电脑上改本地解析,不能代替这一步。证书机构会从它自己的网络来访问你,它看不见你笔记本上的临时对照。

证书回答的是:浏览器愿不愿意相信“这家网站就是这个名字”。证书里的名字必须覆盖访客正在使用的主机名。页面内容再正确,名字不在证书里,浏览器也会拦住。拦住发生在页面之前。服务器的访问日志里,可能根本没有这次失败对应的正文请求,因为握手没有完成。

页面回答的是:请求到达之后,返回的是不是你的站点。它由入口里的站点规则决定。证书安装成功,不代表规则把这个主机名送到了新应用。规则写错时,访客会在一张完全有效的证书后面看见邻居的网站,或者看见一个默认页。那不是证书坏了,是这一层没有认领这个名字。

三层可以分别成功,也可以分别失败。解析已经指向你,证书却只写了一个名字。证书里两个名字都有,入口却把 www 送进了旧站点。两个加密地址都能打开正确的文章,未加密的地址却把人送到另一个主机名。验收如果只看其中一层的绿灯,另外两层的缺口会留到读者那里才出现。

所以顺序是固定的。先让两个名字从公网指向同一台机器,再让一张证书同时写上这两个名字,然后让现有的八十端口交出验证文件,最后才让跳转和页面规则生效。后面的步骤提前做,只会让失败的原因叠在一起,你分不清是哪一层先断的。

还可以用三句问话把现场记下来。这个名字现在解析到哪里。握手时出示的证书里有没有这个名字。这个名字的正文是不是你的应用,而不是旁边那个旧站。三句都有肯定的答案,这条入口才算通。少一句,就还停在半路。

访客带来的名字就是要证明的名字

浏览器核对的不是你喜欢的品牌写法,而是地址栏里那一串主机名。example.com 和 www.example.com 对证书来说是两个名字。多出来的四个字母不是装饰,也不会被自动忽略。只签下其中一个时,另一个即使指向同一台机器,安全警告也照样出现。

读者没有义务改用你后来选定的写法。他从一封旧邮件点进来,邮件作者当年抄的是带 www 的地址。他不该先读懂你的命名偏好,才能看见文章。把其中一个名字做成永远的跳板,可以是你的决定,但那是后文要单独写下的决定,不是证书可以偷懒的理由。即便最终要把人送走,送走之前的这一跳,仍然发生在当前这个名字之上。当前这个名字必须先被信任。

团队内部也常常各记各的。写文档的人用根域名,做印刷品的人习惯带 www,因为多年前有人告诉他“正式网站都带 www”。这两种习惯会同时存在很久。与其在公告里要求所有人改正记忆,不如让两个习惯都落到同一份可以阅读的页面上。公告可以建议一个首选名字,用来统一新印刷的材料和对外介绍。公告代替不了证书。

搜索引擎、聊天工具和社交预览抓取的也是它们遇到的那个主机名。抓取程序不会因为你心里有一个“真正的地址”就自动改口。它拿到哪个名字,就要求哪个名字的证书。预览抓取失败时,读者看到的是一张没有摘要的链接,他不会知道失败发生在握手里。

大小写不会帮你制造出新名字。域名比对时不区分大小写,Example.com 和 example.com 是同一个名字。证书里写成小写即可。端口不是名字的一部分。证书不负责证明“八零端口”或“四百四十三端口”,它只证明主机名。有人把端口写进证书的名字栏,那是无效的额外标签,不能代替真正的主机名。

末尾的点是域名查询里的写法,表示这个名字已经完整,后面不要再追加搜索后缀。证书里的使用者名称不带那个点。你在笔记里可以写带点的查询记录,抄进证书申请时要去掉。多一个点,核对就会失败,而页面本身一点变化都没有。

解析只负责把人送到门口

地址记录回答的是网络位置,不是网站内容。一条记录指向了你的机器,只说明数据包会来敲门。门里谁来应答,是入口程序的事。证书机构来验证时,也是先按记录找到门,再向八十端口要一个文件。记录若指向别处,你在这台机器上准备的文件就不会被读到。

根域名和 www 都要有自己的公开记录。www 是根域名下面的一个标签,不是根域名的别称。只配置了根域名时,带 www 的查询会得不到地址,浏览器连门都找不到,也就谈不上证书。两条都配置了,但指向不同的机器,则是两条不同的门。你在新门上挂的牌子,修不好旧门上的空缺。

如果同时公布了两种网络地址族,两种都要指向同一台准备好的机器。只核对了其中一种,另一种仍可能把证书机构或一部分访客送到旧入口。证书机构不一定总从同一种地址族来。你自己的浏览器刚好走了通的那一种,不能证明另一种也通。验收时两种都看一眼,比事后从失败信里猜要快。

生存时间决定别人要缓存这条记录多久。准备改指向之前,先把生存时间调短,并等旧的生存时间走完,再做真正的修改。顺序反了,短生存时间要等下一轮才生效,你仍会被旧缓存拖住。变更稳定之后,再把生存时间调回日常的长度。调短是为了少等,不是证书规则的一部分,也不该长期留在很短的数值上,否则每次查询都增加无谓的往来。

本机的对照文件只影响本机。你在自己的电脑上把名字指到新机器,只能让自己看见新页面。证书机构、同事的手机、家里的网络,仍然看见公开的记录。用本机对照做演练是有价值的,后文的练习会这么做。用它代替公开记录,则会制造一种只有你能打开的假象。

递归解析器会缓存。你在权威侧改完,立刻在同一个解析器上再查,可能还是旧答案。多等一个生存时间,或者换一个不共用刚才那份缓存的网络再查。看到新答案一次还不够。过几分钟再查一次,确认不是偶然命中了已经更新的那一处缓存。两次从外部看到同一结果,才说得上稳定。

还有一种分裂的视图:办公室内部的解析把名字指到内网,公网的解析把名字指到别处。人在办公室里验收会全绿,证书机构走的是公网,看到的是另一套。这种差异不一定是故障,有的网络故意这么分。它只是不能被当作公网已经就绪的证据。验证前的那一次查询,要从不在这套内部视图里的地方发出。

两条记录先指向同一台机器

申请证书之前,先确认两条记录都落到这台机器,而不是先申请、再指望验证碰运气。其中一条若还指向旧的入口,证书机构会向旧入口要文件。旧入口没有这个文件,验证失败。更麻烦的是旧入口碰巧有一个同名路径,验证居然通过了,你却不知道文件是在哪里被读到的。通过的那一次并没有证明新机器上的配置是对的。

所以要先改记录,再等外部解析稳定,然后才申请。稳定是指:从不属于你这台机器的网络看,两个名字都指向你即将提供页面的那一台。只在机器自己上面查询,可能查到的是它的内部视图,或者是它刚刚刷新的缓存。换一条网络,是这个步骤里最便宜的保险。

改记录不等于更换整组名称服务器。名称服务器决定谁有权回答这个域的所有记录。更换它,影响面是整个域,包括邮件、旧站点和你还没想起来的那些名字。这次任务只需要两个主机名的地址记录。若名称服务器本身就是错的,那是另一次变更,应该有自己的清单和回退,不要夹在证书申请里面。

www 可以是一条独立的地址记录,也可以是指向根域名的别名,只要最终落到同一台机器。别名会多一次查询。根域名改了,别名跟着走,这是它的好处。别名若还留在旧的目标上,它不会因为根域名改对了就自动改对。申请前把别名的最终结果查出来,看到的是地址,而不只是别名本身。只看到别名那一行就以为已经对齐,是常见的漏查。

根域名本身通常不能做成一条普通的别名记录,因为这个名字上还承担着域的其他基础记录,两者不能挤在同一处。有的域名服务提供“别名式”的合成记录,对外仍表现为地址。你可以使用这种服务,但要确认合成出来的结果从外部看就是你的机器。不要假设服务商的默认目标一直是你昨天选中的那一台。

若一条记录已经指向这台机器,另一条还在传播,就等到两条都稳定再申请。只让已经就绪的那个名字先拿到一张单名证书,看起来像进度,实际会让你稍后面对两张证书、两次续期和一次“哪张文件才被入口使用”的混淆。这篇选择的做法是等齐,然后一次签下两个名字。

改完记录之后,不要立刻去重载正在服务的网站。解析的变更不需要网站进程做任何事。过早重载,反而让一次与邻居无关的等待,变成对所有站点的动作。等待的这段时间里,旧站点应该和往常一样应答。

两个名字都写进同一张证书

证书的使用者可选名称里,需要同时包含根域名和 www。现代浏览器用来核对的是这份清单,而不是只看证书最上面那个单独的通用名称。通用名称里写了根域名,不会自动覆盖 www。清单里少谁,谁就会在握手时被拒绝。

一张证书写上两个名字,比两张各写一个更适合这对入口。续期只有一次申请,入口只需要记住一对文件。两张证书也能工作,但你要分别申请、分别到期、分别防止装错文件。装错的表现很迷惑:其中一个名字完全正常,另一个突然拿出邻居的身份,或者拿出已经换掉的旧身份。一张证书消除的就是这种错配。

名字清单是精确匹配。写上 example.com,不会隐含 www.example.com,也不会隐含 blog.example.com。你打算服务哪些主机名,就写哪些。不要为了“以后也许用得上”把还没指向这台机器、你也还不能提供验证文件的名字加进去。多一个名字,就多一次独立的验证。那一次失败,整张证书都不会签发,包括其实已经准备好的那两个。

清单里的顺序不重要。根域名写在前面或 www 写在前面,浏览器都同样接受。不要为了顺序去重签。笔记里可以按你喜欢的顺序记录,方便人读。

申请时使用的账户和这张证书的私钥不是同一把钥匙。账户用来向证书机构证明“是同一个申请人在操作”,证书私钥用来在握手时证明“这台服务器持有这张证书”。续期时两者都要在,但它们放在不同的位置,入口配置只需要后者和证书链。不要把账户那把钥匙抄进站点配置,也不要把它和证书文件放在会被网页直接列目录的地方。

签发完成后,名字清单就冻结在这张证书里。之后再增加第三个主机名,不是编辑原文件能完成的,而是一次新的签发。因此申请前把两个名字核对清楚,比签发后发现少了一个再来一轮要省事。少了一个的补救仍然不要去停共用服务。补救就是再走一次同样的验证路径,把两个名字一起放进新的申请里。

通配符盖不住根域名

星号只能代替主机名里的一个标签。一张写着 *.example.com 的证书,可以覆盖 www.example.com,也可以覆盖 blog.example.com,但不能覆盖裸的 example.com。根域名没有那一个可供代替的标签。很多人记得“星号什么都能配”,于是以为已经有了通配证书就不必再管根域名。访客打开根域名时,警告会纠正这个记忆。

星号也不跨层。*.example.com 不覆盖 a.b.example.com,因为那里有两个标签需要匹配。你如果只打算提供根域名和 www,就不需要研究多层通配。把精力放在这两个精确名字上,清单更短,验证也更少。

通配证书不能靠这篇使用的网页文件方式来申请。它要求你在域名记录里放下一条临时的文本记录,证明你能改这个域。那是另一种挑战,适合真正需要无限子名字的场合。它也会把“能改域名记录的凭据”放到申请流程里。对一台已经有许多站点的机器来说,多一套域名凭据,比在八十端口上提供一个临时文件更重。这对入口不需要通配,就不要为了省事去碰那套凭据。

即便你已经持有一张通配证书,根域名仍要单独出现在某一张证书里。可以把根域名和 www 一起签进这篇所说的这一张,让入口对这两个名字只指向这一对文件。也可以让 www 继续用通配证书、根域名用另一张。两种都能通,但一个入口使用两张证书,续期和排错都要记两套日期。新做这一对时,一张证书同时写上两个精确名字,是更少分支的选择。

不要用通配去“顺便”覆盖旧站点的名字。旧站点若已经有自己的证书和自己的规则,它不需要被卷进这次申请。通配一旦覆盖了不该你签的名字,验证会去访问那些名字的八十端口。那些端口上的站点会被无故探测,失败时还会挡住你真正要签的名字。精确列出两个名字,范围就锁在这次的任务上。

有人希望一张证书覆盖整个域的所有子名字,再加根域名。那是一次更大的签发,验证方式也不再是这篇的网页文件。把它和“根域名加 www”混在同一次变更里,失败时你不知道该检查文件路径还是该检查文本记录。一次只做一种形状。这篇的形状是两个精确名字,外加已有八十端口上的文件验证。

握手先于任何跳转

一次访问的顺序是固定的。先解析,再建立连接,再在加密握手里出示主机名,服务器据此选择证书,浏览器核对证书里的名字。都通过之后,才会有网页请求,也才会有跳转。你不能用跳转去修补一张对不上名字的证书,因为跳转还没有机会发生。

这就是“即便要把 www 永久送到根域名,证书里仍然要有 www”的原因。永久送走是网页层的决定。网页层还没开始时,浏览器已经看过证书了。证书里没有 www,访客停在警告页,他甚至不会见到你精心写的跳转。警告页上的按钮要求他承担风险继续,这不是你该让读者做的选择。

自签证书和名字不匹配是两种不同的警告。自签是签发者不在信任链里。名字不匹配是签发者也许可以被信任,但这张证书没有承诺当前这个主机名。练习时两种都要见过,正式环境里两种都要避免。不要用“先点继续”来完成验收。继续之后的页面,只能证明内容存在,不能证明身份被接受。

有的浏览器会记住一个名字的严格传输状态。一旦记住,它会在以后的访问里跳过未加密的地址,直接去握手。这记忆属于那个主机名,不是属于你的服务器。你在服务器上改了规则,旧的记忆仍在读者的浏览器里。因此,在两个名字的证书都就绪之前,不要让根域名宣布“连同所有子名字一起,以后只走加密”。www 是根域名的子名字。宣布得太早,读者的浏览器会直接找 www 的加密入口,而那时 www 可能还没有证书。他会一直失败,直到记忆过期,而你在服务器上已经看不出他为什么不走未加密的那一跳。

这种记忆是后话,不该出现在第一次签发的同一时刻。先让两个名字的加密入口都能独立完成握手,再考虑要不要加上如此强硬的声明。声明一旦被广泛的浏览器记住,收回它比发出它慢得多。这篇的最小目标里没有这项声明。

握手阶段选定证书之后,页面规则才看见路径。所以“路径写对了”救不了“名字不在证书里”。排查时若读者说警告,先看证书清单,不要先去改网页里的链接。链接是更后面的层。

验证文件由现有的八十端口提供

网页文件这种挑战的做法是:证书机构给你一个本次专用的地址,你在自己的网站上用普通的未加密协议提供其中的文件,机构来取,对上了才签发。取文件的位置在八十端口,路径落在 /.well-known/acme-challenge/ 下面。这不是你的文章路径,也不该由应用去生成一篇带主题的网页。机构要的是文件正文本身。Let's Encrypt:验证挑战的类型

两个名字会各被验证一次。证书里写了两个名字,就有两次独立的取文件。两次都要成功,这张证书才会发出。根域名的文件能被读到,不代表 www 的同一路径也能。入口必须在两个主机名下都把这个路径指到验证目录。只在默认站点上配了一次,而某个名字并没有落到默认站点,那个名字的验证就会看见别的内容。

已经在听八十端口的程序,应该继续由它来提供这个文件。不要为了申请,另起一个临时程序去抢八十端口。端口只能有一个监听者。临时程序抢到了,原有的监听者就退出了,上面所有站点的未加密入口一起消失。没抢到,你的申请工具报错,你再去猜是不是证书机构坏了。两种结果都比“在现有程序里加一条很窄的路径”更差。

这条路径要优先于普通的跳转。若未加密的所有路径都被送到加密地址,验证请求也会被送走。第一次申请时加密地址可能还没有可信任的证书,跟随跳转会失败。即便以后有了证书,多一次跳转也多一个失败点。稳妥的做法是:对这两个主机名,凡是验证路径,都直接返回文件;其他路径才跳到同一主机名的加密地址。验证路径不要交给后面的应用。应用不认识这个文件,往往会返回一个包在模板里的“找不到”,证书机构对不上正文,就会拒绝。

证书机构会跟随有限次数的跳转,但最终仍要拿到那段正文。不要把验证路径跳到另一个你控制不了的名字上,也不要跳到一个需要登录才能看的地址。验证没有登录状态。它就是一次普通的取文件。

八十端口若在防火墙上对公网关闭,现有程序在机器里听着也没用。证书机构进不来。这时不要去停掉四百四十三端口上的站点,也不要把应用自己的高端口临时暴露到公网凑合验证。先恢复八十端口对公网的最小可达,并且只让它提供验证路径和你原本就公开的未加密服务。这篇的前提是:八十端口已经由共用的入口在公网上提供。前提不成立时,先补前提。

不要为了申请证书停掉共用服务

共用的入口上不止你这一对名字。旧站点、也许还有别的工具,都从同一扇八十端口的门进来,再按主机名分开。把这扇门关掉,它们全部中断。读者不会知道你正在申请一张与他们无关的证书。他们只会看见自己的网站打不开,然后开始找是谁动了机器。

停掉服务的诱惑在于:申请工具有时提供“我来暂时监听八十端口”的模式。在一台只有一个站点、可以接受短暂停顿的机器上,这种模式很直接。在已经有邻居的机器上,它的直接等于扩大影响面。你要的文件只是一个很小的路径。现有入口完全可以在继续服务其他人的同时,把这个路径交出去。

停服务还有一个更隐蔽的损失:你正在编辑的配置若还没通过测试,服务可能起不来。本来只是新站点的一次申请,变成所有站点都要等你把配置改回上一版。回退本身是该会的技能,但不该被一次验证逼出来。让旧进程一直跑着,新的配置在旁边写好,测试通过再平滑重载,影响面就锁在“读入新规则”这一件事上。

平滑重载的意思是:已经连着的请求尽量做完,新的请求使用新规则。它不是重启整台机器,也不是把所有应用重新拉起来。验证路径这种变更,在测试通过后做一次平滑重载就够了。如果测试没有通过,就不要重载。正在运行的配置应保持原样,邻居应毫无感觉。

有人会在失败后改用“换一个高端口,再让证书机构去连那个端口”。网页文件挑战不会去连你随便选的高端口。它找的是八十。你打开高端口,只是在机器上多开了一扇没有人要的门,对验证没有帮助,却增加了暴露面。不要用多开一扇门来表达“我已经尽力”。

另两种挑战也不是这次的退路。文本记录挑战要求改域名,还常常要求把域名服务的凭据放在申请工具能读到的地方。加密握手挑战要求在四百四十三端口上用一种特殊的方式应答,容易和正在服务的加密入口抢同一扇门。它们各有适用的场合。这里的场合是:八十端口已经开着,两个名字即将指向它,你只需要一条窄路径。守住这个场合,就不要在失败的第一次尝试里改换整套方法。

把“不停共用服务”写进变更说明的第一行。以后的人看到申请失败,第一反应往往是停掉入口再试。第一行若写明这条禁令,以及验证目录的位置,他会先去查路径,而不是先去关邻居的门。

验证目录不要和网站根目录混在一起

验证文件放在一个只属于申请和续期的目录里。这个目录不是任何一个网站的发布根。网站发布时,常常会清空自己的根目录再放入新文件。验证文件若混在里面,一次普通的内容更新就会把它们删掉。删掉的时刻若刚好赶上续期,续期失败。失败的原因看起来像证书机构拒绝了你,其实是文件在几分钟前被发布流程清掉了。

两个主机名可以共用这一个目录。验证路径在两个名字下都指到这里。你不需要为 www 再复制一份文件。证书机构来取哪个名字,入口都从同一处读。目录里的文件名由申请工具按本次挑战生成,你不要手写一个同名文件去“帮助”它。手写的句子和工具要放的正文不是一回事。

这个目录也不要放在会被应用当作上传位置的地方。上传位置的规则是接受访客写入,验证目录的规则是只让申请工具写入、让入口读取。两者的权限方向相反。混用之后,要么访客理论上能往验证路径放东西,要么应用的清理任务会把验证文件当垃圾去掉。单独的目录、单独的权限,这两件事就拆开了。

入口读取这个目录时,按文件原样送出。不要套上网站的页眉页脚,不要注入统计脚本,也不要因为“全站都要登录”就把这个路径拦到登录页。证书机构不携带登录状态,也只比对正文。多出来的一个网页骨架,就会让比对失败。

发布流程的排除清单里要写上这个目录。若发布用的是“使目录变得和仓库一致”,那么验证目录必须在仓库和发布范围之外。人靠记忆在发布前把文件挪走,迟早会忘。用范围本身保证它不会被碰到。

验证结束后,工具通常会自己清理本次的文件。你也可以在确认签发成功之后清掉残留。这些文件不是页面,不需要长期留在网上。它们也不该被收进备份后当成站点内容的一部分来恢复。恢复站点时把旧的验证文件恢复出来,没有好处。恢复证书时要恢复的是那一对证书文件,不是这个临时目录。

目录的权限保持在“申请工具可写、入口可读、其他人不可写”。不要为了图方便开成任何人都能写。验证路径是公开可读的,这是挑战本身的要求。可读不等于可写。公开的是证书机构要核对的那一段内容,不是让路人修改你的验证目录。

先用手写文件把失败分成两类

正式申请之前,在验证目录里放一个你自己写的短句,文件名选一个不会和正式挑战撞车的名字。然后从外部,用两个主机名分别去取这个文件。两次都应该得到成功的状态,正文就是你写的那一句,一个字不多,一个字不少。这一步还没有接触证书机构。它只证明路径接上了。

若其中一次拿到的是别的网站的页面,说明这个主机名没有走到你刚写的规则,可能落到了默认站点。若拿到的是跳转,看跳转目标是不是仍在验证路径上,还是被总的跳转规则抢走了。若拿到的是应用的“找不到”页面,说明请求被转给了应用,而不是验证目录。这三种结果对应三种不同的改法,都不需要去改申请工具的参数。

再用旧域名取同一个路径。旧域名不应该返回你的短句。若返回了,说明你把验证路径配到了过宽的地方,所有主机名都共用了它。收窄到这一对新名字上。旧站点自己的 /.well-known/ 若已有别的用途,更不能被你的目录盖住。收窄是为了让邻居的已知路径保持原样。

从机器内部取到文件,只能算内部通路是通的。证书机构不在机器内部。请从另一条网络再取一次。内部取文件成功、外部连接被拒绝,是防火墙或外部地址没有指到这台机器。内部取文件就失败,则还没到检查防火墙的时候,先修入口规则。这个顺序可以避免你为了外部问题去改已经正确的配置。

短句测试通过之后,删掉这个手写文件,再让工具去申请。不要把手写文件留到正式验证里碰运气。正式验证的文件名和正文都是工具按本次挑战计算的。你的短句只是探针。探针完成任务就退出。

把两次成功的取文件记成短行:哪个地址、状态码、正文是否一致。后文的验收短行会沿用同一种记法。现在先记下“验证路径”这一组。它和“页面是否正确”是不同的组。混在同一句“都能打开”里,下次续期失败时你无法知道该组曾经通过。

若短句测试在其中一个名字上失败,就不要开始申请。申请不会把路径修好。它只会把同一个失败再演示一遍,并且可能计入失败次数。先把两个名字的探针都做成成功,再进入会联系证书机构的那一步。

未加密到加密时保留当前主机名

两个名字都应该能停下来阅读时,跳转只改协议,不改主机名。访客从根域名的未加密地址进来,就到根域名的加密地址。从 www 进来,就到 www 的加密地址。路径留着,查询留着。地址栏里的名字保持他刚刚使用的那一个。

http://example.com/文章        →  https://example.com/文章
http://www.example.com/文章    →  https://www.example.com/文章
绝对链接的基准地址              →  只选其中一个,写进应用配置

写死成其中一个名字,看起来整齐,效果却是把另一个名字永远当成跳板。若这正是你想要的,可以明确这么做,但那是下一项决定,而且证书里仍然要有两个名字。若两个名字都应该能读书,就不要在跳转里把人送走。送走一次,他的书签、他复制出去的链接,都会渐渐变成你写死的那一个。你也许希望如此,但应该是你选择了基准地址,而不是一条过于热心的跳转替你做了选择。

拼接时使用这次请求里的主机名,而不是配置文件里的常量。常量会在你复制站点规则时被一起复制,于是两个名字都跳到同一个地方。请求里的主机名会跟着访客走。规则只声明“协议升级到加密,其余保持”。这样的规则对两个名字是公平的,也不需要为每个名字各写一条目标不同的跳转。

跳转的目标要是完整地址,包含协议和主机名。有的客户端对只有路径的目标位置处理得很不一致。你已经知道正确的主机名,就把它放进目标里。不要让客户端自己猜。

入口若同时接受带端口的主机名,注意不要把未加密的默认端口写进加密地址。访客若从带有默认端口的形式进来,跳转后的地址应该是干净的加密地址,而不是带着一个错误端口的加密地址。错误端口会让他去敲一扇没有加密服务的门。普通的域名访问不会带端口。你在测试时如果手写了端口,要知道那和读者的访问不是同一条路径。

默认站点不要抢走这两个名字。没有写明主机名的请求会落到默认站点。你的两个名字必须被明确认领。否则未加密的跳转可能由默认站点执行,目标却是默认站点自己的主机名。访客从 www 进来,结果去了旧站点的加密地址。证书也许完全有效,内容却不是他要的。这仍是页面层的错误,不是证书层的错误。

路径、查询,以及先用临时跳转

跳转要保留路径和查询。一篇文章的地址后面若带着某一节的标记或一组分页参数,升级到加密之后应该还在。只把人送到首页的加密地址,等于把所有未加密的深链都弄丢了。读者会以为页面被撤掉了。实际上只是跳转写得太粗。

锚点那一段不会被发送到服务器。你在日志里看不见它,也不必设法把它写进跳转。浏览器在跟随到新地址之后,会按自己的规则处理锚点。你的责任是路径和查询。把这两样保留好,锚点才有地方可挂。为了“完整”而自己发明一个包含锚点的目标,反而可能造出不合法的地址。

只读页面用永久跳转或临时跳转,差别在于谁会记住它。永久跳转会被浏览器记住很久。规则若写错了主机名,读者的浏览器会在你改回服务器之后,仍按旧的目标送人。你在服务器上再测试是对的,他的浏览器却还活在错误的记忆里。所以在规则还没被验收短行证明之前,先用临时跳转。临时跳转每次都来问服务器。等两个名字的目标都正确,再改成永久,让以后的未加密访问少一次来回。

这个顺序对这对入口尤其重要。错误的永久跳转常常是“所有人都去根域名”。一旦被记住,你后来决定让 www 也能停留,一部分读者的浏览器却不再给你机会展示 www 的加密页面。他们在本地就被送走了。先临时、后永久,就是在给你自己留一条改口的路。

表单的提交若也被这条规则碰到,永久跳转有时还会改掉提交方法。一个以阅读为主的站点,公开页面都是读取,这个问题不大。若某个路径确实要保持原方法,应使用明确保持方法的跳转状态,并且不要和普通文章的跳转混成同一句模糊的“全部永久”。这篇的公开阅读路径,在确认无误后使用永久跳转即可。把例外写在例外的路径上,不要为了例外把所有文章改成含糊的状态码。

还有一种多余的第二跳。加密入口又因为目录末尾缺少斜线,把人送到另一个主机名。第一跳本身是对的,第二跳把名字改了。检查时不能只看第一下的目标。要看最终停住的地址。最终地址的主机名应仍是访客开始时的那个,协议是加密的,路径与他请求的资源对应。多出来的斜线规范化可以存在,但它应该留在同一个主机名之内。

在改成永久跳转的那一天,把验收短行再做一遍。这一遍专门看目标里的主机名。主机名正确,才允许这条永久规则留下来。没有这一遍,就保持临时。临时多一次来回,成本很小。错误的永久记忆,成本是读者自己的浏览器,你清理不到。

把一个名字收成跳板要单独写下来

有的站点确实希望对外只出现一个名字。搜索结果、名片和口头转述都用根域名,www 只是为了接住旧习惯,接住之后立刻送到根域名。这是合法的策略。它和“两个名字都停留”不同,必须写在配置旁边的一两句说明里,否则下一次有人会按另一篇文档把跳转改回去,两个策略叠在一起变成循环。

选择跳板策略时,证书清单仍然包含两个名字。理由在握手的顺序里,前面已经说过,这里不再重复机制,只重复这条结论:送走之前要先通过当前名字的身份核对。少了 www 的证书,跳板不存在,存在的是警告页。

跳板应当只发生一次,并且发生在加密之后,或者发生在未加密升级的同一次里,但目标必须是你选定的那个加密名字。不要让未加密的 www 先跳到未加密的根域名,再跳到加密的根域名。能并成一次,就不要两次。每一次多出来的跳转都是缓存和循环的材料。

严格传输的声明要和策略一致。若 www 永远跳到根域名,通常由根域名来宣布“以后请直接用加密访问我”。不要在 www 上也宣布一套相反的偏好。更不要在根域名上使用“包含所有子名字”的强硬声明,除非 www 的证书和跳转已经稳定,并且你接受子名字都会继承这条约定。这篇若采用两个名字都停留的策略,就更不要发出那条包含子名字的声明。策略没选定之前,声明先不写。

跳板策略还要通知应用:绝对链接都用那个最终的名字。否则页面停在根域名,页面里的链接又写回 www,读者点一下就被送回跳板,再被送回来。他未必觉得这是循环,只觉得地址栏在闪。闪动来自两处配置各说各话。后文的基准地址就是用来避免这种闪动的。

把策略写成一句人话,放在站点配置顶部的注释里,也放在你的变更记录里。例如:“两个名字都停留,基准地址用根域名。”或者:“www 永久跳到根域名的加密地址,证书仍包含两者。”两句都短。没有这句,只有配置,半年后没人敢动这条跳转,因为它看起来像某种曾经修过的事故。

循环常常不出现在首页

首页可能一切正常。未加密到加密,主机名保留,文章也在。循环出现在更后面的动作里:登录之后的返回、语言切换、表单提交,或者应用认为“外部地址不该是这个主机名”的任何一次纠正。应用若被配置成只承认其中一个名字,它会在这些动作里把人送去它承认的那个。入口又按“保留当前主机名”把人送回来。两边都觉得自己在纠错,读者的浏览器看到的是来回跳,直到它放弃。

这种循环不一定用两个地址无限交替。有时是同一地址不断把自己再跳一次,因为应用看见的协议是未加密的。它在代理后面,连接本身确实没有加密,于是它再次要求跳到加密地址。入口已经在外面做了升级。应用不知道,就再做一次。入口看到的仍是加密请求,又转给应用。应用再次要求升级。从外面看,目标地址没有变化,状态却一直是跳转。

排查时把每一跳的状态码和目标地址按顺序记下来,不要只记最后的失败。同一地址连续出现两次跳转,问题多半在应用对协议的判断。两个主机名交替出现,问题多半在“保留当前主机名”和“应用只承认一个名字”打架。第一种要让应用相信入口已经终止了加密,第二种要让应用不要在阅读页面上改写主机名。基准地址只用于生成绝对链接,不用于把正在阅读的人赶去另一个名字。

登录页是最值得走一遍的路径。它常常在成功之后跳到一个绝对地址。这个地址若是内部端口,读者会看见一个从公网无法继续访问的目标,或者看见证书名字突然变成了别的东西。验收时要真的看一眼登录页会把人送到哪里,即使你不完成登录。能看的部分是:打开登录页时的主机名,以及页面源码里表单的提交地址。提交地址应该是当前主机上的路径,或者是你明确选定的基准地址,不应该是一个只有本机才懂的端口。

循环修起来不要靠停掉共用入口。停掉入口,所有站点的跳转一起消失,循环也暂时看不见了,问题却没有被定位。把两跳的目标记下来,就能知道该改入口的主机名规则,还是该改应用里写死的外部地址。改动应该只碰写出错误目标的那一处。

还要区分缓存里的旧跳转和服务器现在的跳转。用一个不记忆永久跳转的客户端来看服务器的回答。若客户端看到的是对的,而浏览器看到的是旧的,就去处理那条过时的记忆,不要再改服务器。服务器被反复改,最后会连你自己都不确定哪一版是验收通过的那一版。

绝对链接可以另选一个基准地址

两个主机名都能打开页面,并不表示站点地图、订阅源、分享卡片和搜索引擎用的首选标记也要各写各的。这些场合需要一个绝对地址。绝对地址只能有一个主机名。你选一个作为基准,写进应用的配置,让所有生成出来的绝对链接都用它。另一个主机名继续负责接住直接来访的人。

基准地址包含加密协议和你选定的主机名,以及站点若确实挂在某个路径下时的那个前缀。这篇假定站点在域名的根上。基准地址的末尾是否带斜线,全站统一一种,不要在地图里混用。混用会被当成两个地址,缓存和统计都会分裂。

选择哪一个,取决于你希望被人复制的是哪一个。根域名更短,适合作为基准。若你的印刷品和旧资料长期使用 www,也可以把 www 定为基准。没有道德上更正确的一方。有关系的是:选定之后,新生成的绝对链接都遵守它。文章正文、导航和站点地图里不要一会儿一个。读者会以为这是两个网站,或者以为其中一个是镜像站,不确定哪边会更新。

基准地址是应用的设置,不是每一篇文章里的手写前缀。文章里若需要指向本站另一篇,用站内路径。换基准地址时,你改一处设置,而不是去改每一篇旧文。手写的绝对地址会在换基准的那天变成漏网之鱼,有的还指向已经不再使用的协议。

首选标记和跳转策略必须一致。若两个名字都允许停留,首选标记告诉搜索引擎“索引时请用基准这一个”,同时不把另一个名字的读者赶去别处。若你已经让 www 永久跳到根域名,首选标记也应是根域名。不要一边永久跳向根域名,一边在标记里写 www。两种信号相反时,抓取程序会反复确认,结果不稳定。

分享卡片抓取的是绝对地址。卡片上的标题和摘要应来自基准地址的那一次抓取。当有人从 www 分享时,若页面能提供基准地址上的卡片信息,分享出去的链接就是稳定的那一个。这不等于把正在阅读的人立刻跳走。他可以继续留在 www。被分享出去的,是你选定的那条。

订阅源更要稳定。读者的阅读器会记住源的地址。源的地址若在两个主机名之间变来变去,阅读器会当成两份订阅,或者在一份上报找不到。源只用基准地址,并且不要随一次无关的证书续期去改这个地址。证书续期不改变基准。

页面里的链接留在读者当前的主机上

站内导航、样式、图片和文章之间的链接,宜用相对于站点根的路径。这样的路径不携带主机名。读者在哪个名字下打开页面,这些资源就从哪个名字去取。他不会在点开下一篇文章时被悄悄改送到另一个名字。两个入口提供同一份内容,这条规则是内容一致的一部分。

把模板里的每条链接都写成基准地址的绝对形式,页面仍然能读,但每一次点击都把人收到基准那一边。若你的策略是跳板,这恰好是你要的。若你的策略是两个名字都停留,这就在链接层推翻了跳转层的保留。两个层各做各的,读者只看见地址栏变了。所以模板默认用站内路径。只有地图、源、首选标记和分享信息使用基准地址。

协议相对的链接写着“沿用当前协议,但主机名是我指定的那一个”。它会把人钉在你写死的主机上。你想留在当前主机时,不要用这种形式。它看起来像一种折中,实际只折中了协议,没有折中主机名。

样式和图片若被写成另一个主机名的绝对地址,还会带来额外的失败模式。那个主机名的证书若出了问题,页面的文字还在,版式和图片全部消失。使用站内路径时,样式和页面共享同一次握手。要么一起成功,要么一起在门口失败,不会出现“字在而样式来自一个失败的名字”的半残页面。验收时分别检查两个主机名上的样式,就是在确认没有人把样式写死到其中一个名字上。

邮件里的链接必须是绝对的,因为邮件没有“当前站点根”可供相对。邮件使用基准地址。这和网页模板不同,不要为了统一而强迫网页模板也写成邮件那种绝对形式。两种媒介的约束不同。在配置里可以是同一个基准地址被两处读取,但网页模板仍然输出站内路径。

有时文章正文会引用本站的某一页,作者顺手粘贴了完整地址。那是内容,不是模板。可以在发布前把本站的绝对地址改成站内路径,避免一篇旧文把读者带去当时的主机名。若完整地址已经印在引用里,而主机名是两个之中的一个,它仍然能打开,因为两个你都准备了证书。这是保留两个名字的好处:旧文里的绝对地址不会变成警告页。它可能不是基准地址,但读者能读。你不必为了纯洁而去改写所有历史正文。新的生成规则守住基准即可。

查询失败和没有授权记录不是一回事

证书机构在签发之前,还会查询一种授权记录,用来询问:这个名字允许哪些机构为它签发。你从来没设置过这种记录,通常表示你不限制机构。查询本身失败,则是另一回事。机构无法知道你是否做了限制,按规定只能拒绝签发,而不是当成“没有限制”继续。这两种结果在粗心的工具里都可能被说成“没有记录”。一定要看查询的状态,而不只看答案的行数。Let's Encrypt:CAA

空的成功是:查询完成了,服务器正常回答,答案里没有任何授权记录。这是可以继续申请的状态。没有记录,就是没有额外限制。你不必为了申请而急着补一条授权记录。补写本身若写错,反而会把原本允许的签发变成禁止。

失败则有很多张脸。名称服务器没有回答、回答了但声明自己失败、拒绝查询、或者中途超时,都是失败。失败时不要去改网页配置,也不要停掉入口。网页没有参与这次查询。授权记录住在域名服务里。域名服务短暂异常时,等它恢复再查。恢复的标志是你能看到一次空的成功,或者看到你确实设置过的那几条记录被正常列出来。

有的工具在失败时显示空白,和真的没有记录看起来一样。它若同时不告诉你查询状态,这个工具就不能用来做这个判断。换一个能显示状态的查询。你要看到的是“成功并且答案为空”,还是“服务器失败”。前者继续,后者等待。把状态和答案一起记在短行里。只写“CAA 为空”四个字,下周你无法判断当时是哪一种空。

反复点击申请不会修好域名服务。每一次申请仍会去做同一次查询。域名服务还在失败时,你只会积累失败的申请,并可能碰到频率上的限制。等到查询恢复,频率限制却还没过去,那是自己给自己加的等待。所以先查询,再申请。查询不是申请工具的私有步骤,你完全可以在申请前单独做。

授权记录的目的,是防止一个你并不承认的机构为你的名字签发。它不证明你的服务器在线,也不代替验证文件。文件挑战和授权查询是两道不同的门。文件对上了、查询失败,仍然不会签发。查询是空的成功、文件对不上,同样不会签发。验收时不要用其中一道门的成功去推断另一道。

若你确实想写授权记录,只允许你要用的机构,那么要写上你正在用的这一家,而不是写一个空的禁止所有人。允许 Let's Encrypt 时,记录里的机构名是 letsencrypt.org。写错机构名,效果是禁止你真正在用的这一家。首次申请的那一天,若你对记录的语法还不熟,宁愿保持“空的成功”,也不要在同一小时里既学语法又申请证书。等证书稳定之后,授权记录可以是一次单独的、可回退的变更。

父域上的授权会沿着名字往上生效

查询不是只看你正在申请的那一个主机名。机构会从那个名字开始,若那里没有授权记录,就向父级看,一层层上去,直到看见记录,或者一直到没有为止。www.example.com 上没有,example.com 上有,那么 www 会受 example.com 上那条记录的约束。example.com 上没有,再往上的父级若有,也会约束它。你只检查了叶子而没有检查父级,可能会漏掉一条你几年前在父级设下的限制。

叶子上一旦有了授权记录,向上的查找就停在叶子。父级的记录不再参与这个名字。因此“在子名字上加一条”不是在父级之外再加一个允许,而是用子名字的整组记录替换父级的态度。只在 www 上允许某一家机构时,www 的其他机构都被拒绝,即便父级看起来很宽松。添加时要写下完整的意图,不要只写半句。

签发普通名字和签发通配名字用的是不同的标签。一条只谈论普通签发的记录,不一定表达你对通配的态度。这篇不申请通配,所以关注的是普通签发那一种。若父级上只有通配的允许,而普通签发没有被允许,你的两个精确名字仍可能被拒绝。把查询结果里的标签名字读完,不要看见机构名对了就以为标签也对了。

未知的标签若被标成必须理解,查询方不理解就得拒绝。不要从别人的例子里复制你不认识的标志位。一条最朴素的允许记录,只声明机构和普通签发,就足够表达“这一家可以签这两个名字”。花哨的复制容易引入必须理解的未知项,结果是谁也不能签。

父级若是你和别人共用的域名后缀,你通常改不了它,也不该去改。你能决定的是自己的域。在自己的域上做一次空的成功查询,确认没有意外的记录,然后再申请。若查询显示父级有记录而你的域没有,就认真读那条父级记录是否允许你要用的机构。不允许的话,在自己的域上明确写一条允许,用来停住向上的查找。这仍是域名服务上的变更,不是网站入口上的变更。不要用停站来回应它。

授权记录也会被缓存。你刚刚删掉一条错误的记录,证书机构仍可能在缓存失效前看见旧的。和地址记录一样,等一个生存时间,再重新查询。在缓存过期前反复申请,看到的是同一个拒绝。

把本次查询的结果抄进变更记录:查询的名字、状态、答案里的标签和机构名,或者“成功且为空”。续期若突然开始失败,把新的查询和这次抄下来的对比。授权记录被人改过,会呈现为签发拒绝,而网站页面仍然一切正常。没有当初的抄件,你很容易去重启入口,而入口从来不是原因。

偶发的验证失败先等,再决定要不要改

两个名字里有一个通过了,另一个因为短暂的解析或网络问题失败,配置往往没有错。证书机构从多个网络位置来看你。你自己的一次取文件成功,说明路径在你的位置是通的,不能保证每一个位置在同一分钟都通。这种失败的特征是:过几分钟再试,同样的配置通过了。它和“记录指向了错误的机器”不同。后者会稳定地失败,而且你从外部取文件时就能复现。

遇到偶发失败,先不要停掉已有网站,不要改成另一种挑战,也不要临时监听另一个端口。这些动作的影响面都比等待大。先看你记下的探针短行。探针若刚刚还是成功的,就等几分钟,再单独查询一次授权记录,然后重新申请一次。两次申请之间,不要改配置。改了配置,你就无法知道通过是因为等待,还是因为修改。

若连续两次都指向同一个错误,就不要再等。去读失败说明里的地址和路径。地址不是你的机器,回去改解析,并等到外部可见。路径对了但正文不对,回去看是不是跳转或模板把验证文件包了一层。授权查询失败,回去等域名服务,或修正那条记录。每一种都有自己的下一动作。没有“全部重来一遍”这个动作。

同一种错误不要在一小时里连续猛击。签发有频率上的限制。限制的具体数字以证书机构当时的公开说明为准,不要靠记忆里的旧数字去赌。你需要知道的是:重复的失败会延长下一次真正可以成功的时间。探针测试不接触签发限额,所以能在本地和外部用探针做的检查,都不要换成一次正式申请。

保留失败说明的原文,但不要保留其中任何钥匙材料。说明里通常有名字、地址、路径和机构看到的状态码。这些足够你对照。把说明和当时的探针短行放在一起。只有说明、没有探针,你会以为是机构看错了。只有探针、没有说明,你不知道机构是否看见了和你相同的正文。

偶发失败过去之后,做一次完整的名字核对,而不是直接宣布结束。通过了,仍要看证书里是不是两个名字都在,到期日是不是新的。工具有时在重试的混乱里签下了你没有注意到的旧请求。看文件本身,是后文的单独步骤。这里只强调:等来的成功,也要经过同一套查看,不能因为“终于绿了”就跳过。

若失败只发生在其中一个名字,而另一个名字的站点已经在用旧证书对外服务,不要为了重试去拆掉那个还健康的名字。新的申请可以在旁边进行。验证路径是附加的,不替换现有的加密服务。这正是不停共用服务的意义:失败的尝试不把成功的邻居变成代价。

签发之后只把这一对文件交给新站点

签发成功后,你得到的是一对文件:一份证书链,一把对应的私钥。证书链用来在握手时出示,私钥用来证明你持有这张证书。入口的新站点只需要知道这两份文件放在哪里。旧站点继续使用它们自己的文件。不要让申请工具自动重写整台机器上的站点配置。自动重写在只有一个站点时很省事,在有邻居时可能改掉别人的证书路径、跳转和转发。

一对文件放在新站点能读到的位置。配置里用明确的路径指向它们,不要靠“目录里最新的那个文件”这种含糊规则。最新有时是一次失败的半截写入。明确的路径加上后文的替换方法,才能保证入口读到的是完整的一对。

证书链里应包含叶子和中间证书。只放叶子时,有的客户端能靠缓存把链补全,有的不能。你用常用浏览器测试可能全好,一个从不缓存中间证书的客户端却失败。验收时要用会核对完整链条的方式看一次,不要只看浏览器的锁图标。锁图标会受本机缓存影响。

私钥文件只给入口的运行身份读取,不要给所有用户读取,更不要放到网站的发布根里。放到发布根里,就有可能被当成普通文件下载。那不是证书的公开部分。证书链是准备出示给访客的,可以放在入口能读的位置。私钥不是。两者可以在同一个受控目录里,但权限不同。笔记里记录权限是否收紧,不要记录私钥的内容。

安装是改新站点的配置,让它在加密握手时使用这一对。两个主机名可以出现在同一段站点规则里,共用这一对文件。也可以分成两段规则,但指向同一对文件。不要分成两段之后指向两个不同的目录,那会在续期时只换了一处。一段规则写明两个主机名,是更少出错的写法。

改完配置,先做整份入口配置的测试。测试失败就不要重载。测试只检查文件能否被理解和证书文件能否被读入,不等于从外部走了一遍验收。它是重载的前提,不是重载的替代。测试通过后,做一次平滑重载。此时新的加密访问应该能出示这张新证书。旧站点的规则没有被这个文件提及,它们的行为应保持不变。若测试工具提示你改了别处,先解释那处改动,再决定是否重载。

不要在这一步重启应用进程。应用不出示证书。证书在入口。重启应用只会带来一次无关的中断,并且让你在排错时多一个变化的因素。入口的平滑重载已经足够让新文件生效。

续期只替换这一对证书文件

证书有期限。公开的这种证书通常大约九十天,习惯在到期前就换发,而不是等到最后一天。具体的提前量以你使用的客户端和机构的当前说明为准。你要在自己的日历里看见的,是这张证书上的到期日,而不是一个口口相传的数字。到期日写在证书里,后文会要求你打开它看。

续期应当沿用同一条验证路径、同一个目录、同一对名字。不要在续期的那天临时改成停站模式,也不要临时加上第三个名字。续期的定义是:为同一对名字再取一对新文件,替换旧的那一对。范围若变了,那就是一次新的变更,应该回到前面的步骤,而不是塞进定时任务。

定时任务可以经常醒来,看看是否到了该续的日子。没到日子,它应该什么都不改,安静退出。到了日子,它向机构申请换发,验证仍由现有的八十端口完成。验证路径在这两个主机名上一直留着,所以续期不需要再去改站点结构。这也是当初把目录从网站根里分离出来的原因:内容发布可以频繁,验证目录保持不动。

换发得到的是新文件。用临时名字写完,确认它们能被解析、里面确实有两个主机名、公钥和私钥互相对应、到期日比旧的更晚,然后再替换正式的那一对。确认失败,就留下旧的一对继续服务,不要用一份半截文件去覆盖。覆盖之后即便你不重载,下一次任何人的重载都会把半截文件读进去。所以替换之前的确认,是续期脚本的责任,不是人在报警之后的补救。

替换时只动这一对。不要整理旁边的证书目录,不要把看起来旧的文件全部删掉,不要顺手升级入口程序。那些可以是别的任务。续期任务的成功标准很窄:这一对被换成新的,而且新的通过了上面的确认。窄,才可靠。

上一对文件在替换后先留一段时间,放在不会被入口读到的名字下。新文件若在验收时露出问题,把配置指回上一对,再测试并平滑重载,就能回到更换前。立刻删除上一对,等于放弃这个很便宜的回退。留存期间不要把上一对又放回原路径。入口若按路径读取,放回去就是在使用旧证书。留存用别的名字,并在笔记里写上那是哪一天换下来的。

续期失败的通知应该发给会处理它的人,而不是自动触发“停掉入口再试一次”。通知里包含名字、失败属于解析、文件还是授权查询,以及最近一次探针短行。没有这些,收到通知的人只能登录后从头猜。自动停站则是把通知变成事故。

内容没有变化就不要重载全部站点

入口多在加载配置时读入证书。文件换成了新的内容,不重新加载,入口仍出示旧的那一对,直到下一次加载。文件的内容没有变,重新加载不会带来新证书,却仍然是一次作用于所有站点的动作。已经连着的请求通常能做完,但重载不是完全无感的。它会重新读入全部规则,任何一处不相关的配置问题都可能在这一刻变成全体的启动失败。所以重载要留在“这一对文件确实变了”的时候。

比较应当发生在重载之前。对正式路径上的证书链和私钥做摘要,和替换前的摘要对比。摘要不同,才在配置测试通过之后做一次平滑重载。摘要相同,脚本退出,不通知入口。定时任务每夜醒来而证书还早,就属于摘要相同的情况。每夜重载一次所有站点,是用全体的风险换一个并不存在的更新。

有的申请工具只在真正换发之后才调用你准备的钩子。这是好的默认。不要再包一层“无论钩子有没有被调用,我都重载一次”的外层定时。那层外皮会把工具的克制取消掉。若你的环境只能用自己的定时任务,就把比较写进任务里。比较失败或文件不可读,不要重载。不可读的文件被重载进去,新站点的加密入口会起不来,严重时影响同一入口进程里的其他站点。

内容有变化时,需要的也只是入口的一次平滑重载,不是所有应用的重启,不是数据库的重启,更不是整台机器的重启。应用不读这对证书。数据库不读这对证书。把它们拉进续期的动作,只会扩大中断。变更记录里写明:本次只重载了入口。下次有人看到应用重启的时间戳和续期一致,就知道那不是这篇要求的做法,而是有人把步骤加宽了。

平滑重载之前的配置测试,要包含“证书文件能被读到”。测试若只检查语法、不读证书,一份损坏的新文件会在重载时才暴露。把读证书放进测试。测试不过,保留旧文件在原处。这里的顺序是:新文件写到临时名、确认、替换、测试、通过才重载。测试若在替换后失败,立刻把上一对放回正式路径,再测试一次,确认旧的仍能通过,然后停下来查新文件。不要在测试失败的状态下重载。

多个站点共用同一个入口进程时,一次重载在进程意义上是全体的。这正是要吝啬使用它的原因。吝啬不等于证书过期了也不加载。内容变了,就必须加载这一次,否则新证书只躺在磁盘上,访客仍拿到旧的。吝啬的是:没变就一次都不要。变了就只做这一次入口重载,并且事先测试。

把“是否重载”写进续期的短行。日期、两个名字、到期日、摘要是否变化、是否重载。一个月后回看,你能回答“昨夜有没有惊动邻居”。不能回答的续期,不算完成。它只是撞上了运气。

打开证书看名字和日期

工具的最后一行写着成功,有时只表示命令没有报错地退出,不表示两个名字都在新文件里。打开证书链本身,读使用者可选名称,确认 example.com 和 www.example.com 都在,且没有意外的第三个名字。第三个名字意味着你的申请范围比计划大,应该弄清它从哪一次请求里来,而不是默默接受。

再读生效时间和到期时间。生效时间晚于机器当前时间,这张证书现在还不被接受。机器的时钟若偏了,会出现“刚刚签发的证书被说成尚未生效或已经过期”。遇到这种警告,先对时,再判断证书。不要立刻重签。重签会掩盖时钟问题,时钟会继续破坏别的东西。

到期时间应落在你预期的那一次换发周期里。若到期时间几乎没有延长,你可能装上了一份旧文件。比较文件的摘要,而不是比较文件名。文件名可以一直叫同一个,内容才是证书。

证书链的叶子和中间证书都要能被读出来。只有叶子时,补上中间证书再替换。不要让访客的浏览器替你补。它们有时会补,有时不会,这会变成一种“有的人说打不开”的投诉。投诉很分散,因为只有没有缓存中间证书的人才会遇到。

私钥是否与叶子匹配,用公钥对照来确认。对照在服务器上完成,输出只需要“匹配”或“不匹配”。不要把私钥内容、指纹以外的材料打进日志,也不要贴进变更记录。不匹配就不要重载。匹配再进入测试和重载。这一步能抓住“新证书配了旧私钥”或“文件复制时拿错了一对”的错误。

浏览器的锁图标可以看,但不能当作唯一证据。它受缓存、受本机的中间证书、也受你上次点过的“继续访问”影响。用一个会严格执行核对的客户端,从外部访问两个名字,让它验证证书。验证失败就记失败。不要用关闭核对的选项去换一个看起来正常的状态码。关闭核对之后的成功,验收里没有意义。它等于你自己宣布不看这层。

把读到的两个名字和到期日抄进短行。短行不包含证书正文的大段编码。大段编码既不便于比较,也容易在传递笔记时泄露不必要的材料。名字和日期足够判断“这是不是这一次的那张”。

分别请求两个主机名的两种协议

验收不是刷新一次首页。至少做四次请求:根域名的未加密、根域名的加密、www 的未加密、www 的加密。四次都从外部的另一条网络发出。在服务器自己上面请求,可能走了内部的捷径,看不见外部的防火墙,也看不见错误的地址族。

未加密的两次,期望是跳转,而不是直接把文章正文返回来。跳转的目标必须是同一主机名的加密地址,路径保持不变。状态在规则还是临时的时候,应是临时跳转。你已经改成永久之后,应是永久跳转。不要只写“会跳转”。写下状态码和目标里的主机名。

加密的两次,期望是成功,并且出示的证书里同时有两个名字。页面内容应是你的站点,而不是默认页或旧站点。用会核对证书的客户端。核对失败时,记下它抱怨的是名字不匹配、链条不完整、还是尚未生效。三种抱怨对应三种不同的文件问题。

若你公布了两种地址族,四次请求在两种地址族上各做一遍。有一种地址族把 www 送到了别处,就会出现“有的网络能开、有的网络不能开”。你的办公网络恰好只用了其中一种,于是你什么也没发现。证书机构在续期时却可能抽到那一种。

再加一次只取验证路径的请求,确认手写探针已经删除,正式挑战的残留也不在。验证路径此时可以是找不到。它不应该是一篇带主题的文章,也不应该把你带到登录页。找不到,表示没有残留,这是可接受的。若它把你跳到加密地址的首页,说明验证路径又被总跳转卷进去了。续期时这会变成故障。现在就收窄。

这四次加上验证路径,仍不是全部验收。它们证明门和身份。内容是否真的是那篇文章,静态资源是否跟着这张证书走,旧域名有没有被波及,是下一组请求的事。不要用这一组的成功代替下一组。

把四次的结果写成四行。行与行之间不要合并成“两个域名均正常”。合并之后,www 的未加密若在下周坏了,你无法从记录里知道它上周的目标地址是什么,也就无法知道是谁把它改掉的。

再看正文、静态资源和一个旧域名

首页可能被缓存,也可能是一个静态占位,碰巧在两个名字上都能打开。打开一篇具体的正文,找一句你知道一定在这篇文章里的话。两个加密主机名上都应该看见这一句。只在其中一个上看见,说明两个名字后面不是同一份内容,或者其中一个被缓存钉在旧版本上。

然后取一个样式或一张图片。期望是成功,而且类型是样式或图片,不是一篇网页错误。错误页被当成样式取回来时,浏览器的状态仍可能是成功的,页面却没有版式。所以要看类型,或者看正文的开头是不是样式,而不是一则抱歉。两个主机名都取同一个站内路径。其中一个失败,多半是链接被写死到了另一个名字,或那个名字的规则没有把静态路径指到同一处。

登录页或任何一个会继续跳转的页面,看最终停住的主机名。它应该仍是两个公开名字之一,并且是加密的。地址栏里不应该出现只有内部才使用的端口。页面源码里的表单动作也不应该指向那样的端口。这一次不必完成登录。你在确认的是方向,不是口令。

最后请求旧域名。用和变更前相同的方式:它原本是加密的,就仍请求加密;原本会跳转,就仍看跳转。状态码和最终主机名应与变更前一致。你若没有变更前的记录,现在立刻记下,当作以后的底片,并坦白这次只能证明“此刻是这样”,不能证明“和昨天一样”。下一对证书续期时,这张底片就有用了。

旧域名的证书不应该被换成你这一对。打开它的证书,名字清单里应该还是它自己的名字,而不是 example.com。若旧域名突然出示了你的新证书,说明默认站点或过宽的规则把你的证书用到了不该用的地方。收窄主机名的认领,让旧域名回到它自己的文件上。这是验收里最重要的负向检查:新的一对只出现在新的两个名字上。

正文、样式、登录方向、旧域名,这四项加上前面的四种协议请求,构成一次最小的上线核对。少一项,就留下一种只在部分读者身上出现的故障。多项都做了,却没有写下来,下周的续期就无法比较。写下来是这项验收的一部分,不是额外的文书工作。

时间点也要分开。入口重载之后可以立刻看旧域名,因为旧域名不依赖你的新文件内容是否被应用读到。新名字的正文要等应用本身就绪再看。应用还在启动时的失败,不要记成证书失败。证书失败在握手阶段就会结束,根本看不见正文。看见了正文,只是内容不对,就去查页面规则和缓存,不要去重签。

用短行把结果留下来

每一行只放一项观察:地址、状态码、最终主机名、证书里是否同时有两个名字、一句附注。附注只在异常或容易误会的时候写。例如“永久跳转,目标主机名正确”或“旧域名状态与底片相同”。不要写“看起来没问题”。

行的顺序固定,下次续期就按同一顺序再测一遍,把新的一列写在旁边。比较变成对齐,而不是回忆。至少这些行:两个名字的未加密、两个名字的加密、一篇正文在两个名字上各一次、一个静态资源在两个名字上各一次、登录页的最终主机名、旧域名的状态和证书名字、验证路径在两个名字上的结果、授权查询的状态。行多一点没有关系。少到不能区分层次,就失去了短行的意义。

最终主机名和你输入的不一样时,附注里写是哪一条规则送走的。是入口的协议升级,还是应用的基准地址纠正,还是一条你忘记的旧永久跳转。不知道是哪一条,就先查配置,不要猜。猜会把第二条规则改坏。

短行放在这次变更的记录里,和证书到期日、基准地址的选择、两个名字是否都停留放在一起。不要只放在一个会过期的聊天窗口里。续期的人可能不是你。他需要看到上一回的行,才能知道这一回有没有退步。

成功的短行也要留。人往往只记录故障。下次故障时,你需要一份曾经成功的对照,才知道“正文句子还在”这件事上周是真的。没有对照的成功无法复现,也不算验收完成。

若某一行失败,先停止继续扩大变更。不要在失败的同一小时里改跳转策略、改授权记录、再升级入口。先让这一行恢复到上一份短行的状态。同时改三处,恢复了也不知道是哪一处起了作用,失败了更不知道是哪一处引入的。短行的价值,就是让你一次只解释一行的差异。

在测试环境用两个假名字走一遍

正式改公开记录之前,可以用两个不会出现在生产域名里的假名字做练习。练习只存在于测试用的解析里,例如只影响你这台测试机的对照,不要把假名字做到公开的域名上。公开的域名会被真的证书机构、真的读者和真的缓存看见。练习不需要它们。

先让两个假名字指向同一份练习页面。只给其中一个名字签一张自签的证书,用浏览器分别打开。记下哪一个被拦住,拦住时的原文是名字不匹配还是签发者不受信任。然后换成一张同时包含两个假名字的自签证书,再看名字不匹配是否消失。签发者不受信任应当还在,因为自签本来就不在公共信任里。这一步是为了把两种警告拆开。你若发现两种警告同时消失,说明你看错了浏览器的提示,或者连到了另一台已经有公共证书的机器。

接着把未加密到加密的跳转从“总是送到第一个名字”改成“送到当前名字”。用两个假名字各打开一次未加密地址,观察地址栏。期望是各自留在自己的名字上,协议变成加密。再把跳转改回写死的名字,观察另一个名字如何变成跳板。两种策略你都见过,正式环境里才不会只因为“配置能跑”就选了你并不想要的那一种。

最后写下两句话。第一句是你愿意对外使用的基准地址。第二句是另一个入口你打算保留停留,还是收成跳板。练习用的假名字可以就着写,正式时换成真实的两个名字。这两句一旦分开写清楚,跳转、证书清单和绝对链接就有了同一份依据。没有这两句,三处配置会各自演化。

练习结束,拆掉测试用的对照,确认你的日常浏览器没有记住假名字的永久跳转或严格传输声明。假名字不该留在你的浏览器里影响以后的测试。生产的公开记录在整个练习期间都没有被修改。这是练习合格的条件之一。

自签证书不要安装到生产入口上。它的用途是让你看见警告,不是让读者看见警告。生产使用证书机构签发的那一对。练习里养成的“打开证书查看名字清单”的习惯,带到生产的验收里。习惯是可以带走的。文件不要带走。

出错时按层往回查

以后读者说打不开,先问他地址栏里的名字和看到的第一句提示,再决定进哪一层。名字根本无法解析,去看地址记录和生存时间,不要去重签证书。连接被拒绝,去看入口是否在听、防火墙是否放行,不要去改页面内容。提示是证书名字不匹配或已过期,去打开正在出示的那张证书,看清单和日期。提示里的证书属于另一个站点,去看这个主机名有没有被明确认领,还是落到了默认站点。

页面能开但内容是旧站点的,证书这一层已经通过了。继续改证书没有帮助。去看站点规则把这个主机名送到了哪里。页面是你的,但样式来自一个警告,去看样式链接是否写死了主机名。地址在两个名字之间闪烁,去看跳转目标和应用的基准地址是不是互相追逐。

旧域名出了问题,先假定是这次改动过宽。回到你改过的那一个配置文件,看有没有出现旧域名的主机名,有没有替换它的证书路径,有没有一条过宽的验证路径盖住了它。不要同时升级系统来“看看会不会好”。那样会把层又叠上。

续期失败时,按失败说明分成三支。说明指向地址不对,查解析。说明指向文件不是预期正文,用探针从外部取验证路径。说明指向授权查询失败,单独做一次查询,分清空的成功和服务器失败。三支都不要以停掉共用服务为第一步。

修好之后,只重跑失败的那一行短行,以及和它直接相关的一行。例如证书清单改了,就重跑两个名字的加密请求,并重跑旧域名的证书名字,确认没有波及。不必把无关的层全部推倒重来。全部重来看起来彻底,实际让你失去“哪一行曾经是好的”这份信息。

把新的短行写在旧的旁边,注明日期和你改了哪一层。过几个月再看,这篇的方法应该能回答三个问题:两个名字是否都在证书里,未加密是否留在当前主机名上,续期有没有在文件没变时惊动全部站点。三个问题都有短行可以指,入口才算按这篇的方式在运行。

把基准地址和停留策略放在同一张卡片上

前面把基准地址、停留策略和证书清单分在不同的层里讲,是为了避免混成一个动作。真正操作时,它们要写在同一张卡片上,否则每个层的负责人会做出局部正确、整体冲突的选择。卡片不必长。它固定这几行:根域名、www、两者是否都写入同一张证书、未加密跳转是否保留主机名、哪一个名字是基准地址、旧域名是哪一个、验证目录是否独立于网站根。

卡片上的每一行都应该能被一次观察否证。证书清单可以用打开证书来否证。跳转可以用一次未加密请求来否证。基准地址可以用站点地图里的任意一条来否证。验证目录可以用发布一次内容之后探针是否还在来否证。不能被否证的句子,例如“我们会注意不影响别人”,不要写进卡片。它无法在续期的早晨被检查。

卡片放在站点配置的旁边,而不是只放在人的记忆里。配置被复制到新环境时,卡片应一起被看见。复制配置而不复制卡片,很容易把写死的基准地址带到一个并不使用那个名字的地方。新环境的第一件事是改卡片上的名字,再改配置,使两者一致,然后用探针和四次请求证明它们一致。

当卡片上的策略是“两个名字都停留,基准用根域名”,应用里就不该出现把 www 纠正成根域名的阅读期跳转。当策略改成“www 是跳板”,入口的跳转就要改,证书清单却不必缩小。改策略的那天,证书可以不动。这是卡片把层分开的好处。你能看出哪一行变了,哪一行没变。没变的那一行不要重新签发,也不要重载之外的任何动作。若策略变更不需要新证书,就不要为了仪式感去续一次期。

卡片也是验收短行的表头。短行使用卡片里的名字,避免有人用别称记录,下次对不齐。别称在谈话里无妨,在短行里要用完整的主机名。www 和根域名差四个字母,差的就是整次握手。

过一个月把卡片读一遍,对照线上的证书和一次未加密请求。线上和卡片不一致时,以你希望的策略为准去改线上,或者以线上为准去改卡片,但要明确你在改哪一个。默默接受漂移,半年后卡片就变成了小说。小说无法指导续期。

中间证书、时钟和缓存各自会说谎

叶子证书、中间证书和浏览器里的缓存,是三种不同的“看起来像证书问题”的来源。叶子是你这次申请得到的那一张,上面有两个名字和到期日。中间证书是把叶子连到公共信任根上的一环。缓存是某个客户端自己记住的旧结论。三者不要混在同一句“证书有问题”里。

链条不完整时,叶子本身可以是全新的、名字也是对的,严格的客户端仍然拒绝。你打开文件若只看见叶子,就去补中间证书,替换这一对里的证书链文件,确认后再按“内容有变化”的规则重载入口。不要因此去重新申请。重新申请得到的仍是一张叶子。中间证书的缺失不是申请范围的问题。

时钟错误会让一张好的叶子被说成过期或未生效。先看机器时间是否接近你手表上的时间。差得很多,先对时。对时之后不需要新的证书文件,但入口若已经因为错误的时间把证书判失败,对时后做一次配置测试。测试通过且文件摘要没变,是否重载要看入口有没有把失败状态卡住。若只是客户端因为自己的时钟报错,而服务器时钟是对的,就去修那一个客户端,不要重载服务器。服务器的重载帮不了读者手表。

客户端缓存会记住旧的证书错误、旧的永久跳转和旧的严格传输声明。清掉那一个名字的站点状态,再试一次。试之前用一个从不访问过这两个名字的客户端对照。对照客户端是对的、原来的浏览器是错的,问题在缓存。两个客户端都是错的,问题在服务器正在出示的文件。这个对照比反复刷新同一个浏览器便宜得多。

公共的在线检查工具可以当作另一个客户端。它从它的网络来看你,能帮助发现你办公室里看不见的地址族问题。它不是权威。它自己也有缓存,也有可能暂时失败。用它的结果作为短行的一行,而不是作为唯一的判决。你自己的外部请求仍然要做。

不要把中间证书的正文贴进公开的文章或工单的附件里到处转发。需要传递的是“链条是否完整”这个结论,以及到期日和名字清单。文件本身留在服务器的受控目录里。结论进短行。

一种地址族指向别处时会怎样

只核对了一种地址族,是验收里最容易留下的缝。你的浏览器和测试用的网络可能只使用其中一种。另一种仍指向旧机器,或者指向一个已经不存在的地址。证书机构的某一个检查点若刚好使用那一种,验证就会失败。失败说明里的地址和你平时看到的不一样。你若坚持“我这边是好的”,就会开始改不相关的配置。

所以在卡片上写下:这个名字公布了哪些地址族。每一种都从外部查一次,确认落到同一台机器。没有公布的那一种,不要在验收里假装它存在。有的网络会为不存在的记录合成一个答案。合成的答案不是你的配置。使用会区分“没有记录”和“有记录”的查询,把结果记成短行。

从一种地址族改到另一种,或者新增一种,是解析层的变更。它不要求新的证书,因为证书里没有地址。它要求你重新做验证路径的外部探针,因为证书机构可能改走新的地址族。探针成功之前不要申请。新增地址族的那一天,旧站点若也被同一种方式公布,要确认你没有只改了一半的名字。www 新增了,根域名没有,两个名字又会分裂。

关闭一种地址族同样要小心。关闭之后,仍有缓存按生存时间记着旧地址。在生存时间走完之前,仍会有访客走到旧地址。旧地址上若已经没有服务,他们会看到连接失败,而你在新地址上一切正常。把生存时间事先调短,仍然是解析层的准备动作。不要用证书续期来解决缓存。

把地址族的短行和证书的短行分开放。地址族的行里没有到期日。证书的行里没有网络地址。放在一起的表格容易让人以为换了证书就要改地址,或改了地址就要重签。它们只在“验证时机构会按地址来取文件”这一点上相遇。相遇不是同一层。

默认站点会把你的证书借给没有认领的名字

入口通常有一个默认站点,用来回答那些没有被任何规则认领的主机名。默认站点有自己的证书。一个被你忘记写进规则的名字,会拿到这张默认证书。浏览器于是说:证书是签给别的名字的。你查看新站点的配置,会发现文件都对,因为文件确实在新站点里,只是这个请求没有进入新站点。

两个新名字都必须写进明确的规则。不要依赖“排在配置文件前面就会被选中”。选中靠的是主机名匹配,不是靠你阅读文件时的视觉顺序。写测试时故意用一个尚未认领的假名字访问,确认它落到默认站点,并且不出示你的新证书。再访问两个真正的名字,确认它们出示新证书。一正一负,认领的边界就清楚了。

默认站点也不该使用你这一对新文件。它若使用了,所有未认领的名字都会出示 example.com 的证书,警告信息会变成“你访问的是甲,证书却是乙”,并且乙是你的站点。这会把你的名字暴露到不相关的请求上。让默认站点保持它原来的证书,或者保持它原来的拒绝方式。这次变更不改进默认站点,只是不借用它。

有的默认站点会对所有未加密请求做跳转,把人送到某个固定的加密名字。你的验证路径若没有更具体的规则抢先,会被这次跳转带走。前面要求验证路径在两个新名字上优先返回文件,就是为了避开这个默认行为。负向测试用一个无关名字访问验证路径,确认它不会返回你的验证目录。正向测试用两个新名字访问,确认会返回。两边都要。

改默认站点来“让一切更合理”,超出了这篇的范围。默认站点的行为可能是多年前为了接住错误链接而设计的。你今天的理解不一定覆盖当时的原因。只要它不吞掉你的两个名字,就让它保持原样。把这个决定写进卡片:默认站点未改。验收时用旧域名和那个假名字证明它未改。

发布日和续期日不要选在同一时刻

内容发布会替换网站根目录里的文件,可能重启或重载应用。证书续期会替换另一对文件,并可能重载入口。两件事都合法,放在同一分钟就会让失败无法归层。页面若在那一分钟出错,你不知道是发布把应用弄坏了,还是重载把入口弄坏了。

把两者的时间错开。发布有自己的窗口。续期的定时任务放在几乎没有发布的时刻。续期若发现验证目录不在,应失败并通知,而不是去网站根里临时找一个地方放下文件。临时找地方,会在下一次发布时再次丢失。

发布流程的检查清单里加一行:不触碰验证目录,不触碰证书一对文件,不重载入口,除非这次发布本身改了入口规则。普通的文章更新不改入口规则,因此不重载入口。这条和“证书没变就不重载”是同一种吝啬。入口的重载不是表达“我做完了”的仪式。

若某一次发布确实改了跳转规则,它应该带上自己的验收短行,并且不应该同时换证书。跳转的短行变化能够解释地址栏的变化。证书的短行保持不变,证明你没有顺便换文件。一次提交解释一种差异。这是短行对齐之后自然得到的纪律。

续期日若碰巧和发布日撞在一起,让其中一个改期。证书还没到最后一天时,续期改期是安全的。已经到了最后一天,就先续期并完成它自己的短行,再开始发布。不要并行。并行省下的那几分钟,会花在事后的归层上。

频率限制、失败次数和你自己的重试

证书机构对重复的签发和重复的失败有频率上的限制。限制是为了让服务能被大家共用,不是针对你的站点。你不需要背下每一个数字。你需要一条操作上的纪律:探针能回答的问题,不要用正式申请去回答。正式申请的失败说明,读懂之后再决定下一次,而不是立刻再来一次。

同一对名字在短时间里成功签发太多次,也会碰到重复签发的限制。练习应该用自签和假名字,不要用生产名字向真正的机构反复签发来“看看行不行”。生产名字的每一次成功签发都应当对应一次你打算装上入口的替换。签了不用,既浪费限额,也让你分不清哪一对才是正式路径上的文件。

失败说明里若明确写着频率限制,就停止申请,按说明里的时间等待。等待期间可以把探针和授权查询做完,把卡片和短行补齐。这些不消耗签发限额。等待结束之后,只申请一次。这一次之前,验证路径必须已经从外部成功取到探针。没有探针的等待只是在空等。

不要同时用两个客户端对同一对名字申请。两个客户端会互相覆盖验证文件,或者各自留下半截状态。选定一个续期工具,让它独占那个验证目录和那一对正式文件。别的工具可以存在于练习环境,不要指向生产的目录。

频率限制不是停掉共用服务的理由。限制到来时,现有证书若还没过期,站点应继续用旧的一对对外服务。读者不受你的限额影响。这是“上一对文件先留着”的另一个好处:新的签发被限制时,旧的仍在正式路径上,入口不需要任何动作。若你已经用坏文件覆盖了正式路径,限制会让你无法立刻签出替换,站点就真的危险了。所以覆盖之前的确认,和限额是连在一起的谨慎。

记录里留什么,不留什么

变更记录要让下一个续期的人能重复你的判断。留下:卡片上的策略、证书的两个名字、到期日、是否重载入口、验收短行、授权查询是空的成功还是有具体记录、验证目录与网站根分离的事实。这些都是结论和观察。

不留下:私钥的内容、账户钥匙的内容、任何口令、验证文件的完整正文、从证书文件里整段复制出来的编码。验证正文是一次性的,复制到记录里没有用,还可能让人误以为下次还能用同一段。私钥和账户钥匙一旦出现在记录里,记录的传播范围就变成了钥匙的传播范围。你无法控制笔记会被转发到哪里。

可以留下文件的摘要和权限是否只允许入口读取。摘要不能还原私钥,却能回答“正式路径上的文件是不是昨夜替换的那一份”。权限用一句话描述,例如“仅入口身份可读”。不要把身份的登录材料抄进来。

失败说明若包含机器的内部路径,抄录时只保留你自己能看懂的那一部分:哪个主机名、哪种失败、状态码。内部路径对读者没有意义,也不该出现在可能被公开的材料里。这篇本身不记录任何一台具体机器的路径。你的私人笔记若需要路径,就留在私人的地方,不要回写到站点的公开文档。

短行里的地址使用你的真实主机名,这是必要的,因为短行就是要核对它们。不要把短行贴到公开的网页上当成示例。公开示例继续用占位名字。真实短行放在变更记录里。两者不要混成一份会随文章发布的文件。

日志同样遵守这条界线。入口的访问日志可以记录主机名、路径和状态码,这对排错有用。日志不要记录私钥,也不要把整段证书编码打在每一行里。每一行打一遍证书,日志会胀,并且把不该扩散的材料扩散到日志的存放处。需要时,单独用一条命令读证书的名字和日期,把结论写入短行。

邻居站点的边界就停在主机名上

同一台机器上的旧站点,和你的新站点共享入口进程,不共享证书,不共享网站根,不共享跳转策略。边界用主机名来画。请求里的主机名属于旧站点,就完全按旧规则。属于两个新名字之一,才使用新的一对证书、新的跳转和验证目录。

不要用“所有流量先到我这里,我再分发”的方式来表达友好。那会让旧站点的加密身份依赖你的证书。你的证书里没有旧名字,旧访客就会看到警告。分发应该由入口按主机名完成,在握手时就已经选对了证书。握手之后才来分发,证书已经出示错了。

旧站点的续期也不要并进你的定时任务里“顺便做了”。它的名字、它的验证方式、它的文件路径,都可能和你不同。并在一起,你的任务就不再是“只替换这一对”。失败时两个站点的证书一起停在不确定的状态。各管各的一对文件。时间上甚至可以错开,避免同一次重载里换了两对,出了问题不知道是哪一对引起的。

你为新名字做平滑重载时,旧站点会经历同一次进程级的重载。这是共享入口的代价,也是为什么没变就不要重载。重载是不可避免的共享时,至少让它稀少、经过测试、并且只在你的文件确实变化时发生。不要用“反正重载很快”来为每夜的仪式辩护。快,仍然是全体一起经历的一次加载。

若旧站点的负责人不是你,在变更前告诉他窗口,在变更后把旧域名的短行发给他。短行是同一行字,不是一段“应该没问题”。他可以按那一行自己复核。没有这行,他只能从读者的投诉里知道你做过变更。

边界也包括日志和统计。不要把旧站点的访问记进新站点的日志文件,也不要让新站点的日志路径覆盖旧的。日志文件写错地方,看起来像证书问题无关,却会在磁盘满时连累入口。磁盘满了,入口可能无法重载,续期的新文件也就无法生效。给新站点自己的日志,并给它大小上的限度。这是邻居边界的一部分。

一张可以照着做的顺序

把前面的层收成一张顺序,避免操作时来回跳。每一步都有停止条件。停止条件没满足,就不进入下一步。

第一步,写下卡片:两个名字、停留策略、基准地址、旧域名。卡片没写完,不去改任何记录。第二步,从外部确认两个名字的每种已公布地址族都指向这台机器。有一条不是,就只改解析并等待,不申请证书。第三步,在现有八十端口上为这两个名字准备独立的验证目录,用探针从外部取到短句,并确认旧域名取不到这段短句。探针失败,只改入口的路径规则,不停站,不申请。

第四步,单独查询授权记录,确认是空的成功,或是明确允许你要用的机构。查询失败就等待,不改网站。第五步,让工具为这两个名字申请,验证文件由那个目录提供。失败就按说明归到解析、文件或授权查询,修那一支,再决定要不要申请第二次。第六步,把得到的一对文件写到临时名,确认名字、日期、链条和密钥匹配,再替换正式路径。确认失败就不替换。

第七步,只改新站点的配置去使用这一对,测试整份入口配置。测试失败不重载。第八步,测试通过才平滑重载入口,不重启应用。第九步,做四次协议请求、正文、静态资源、登录方向和旧域名。任何一行失败,就停在这一行所属的层。第十步,把短行和是否重载写入变更记录。文件内容若在以后的夜里没有变化,定时任务不得再做第九步之前的重载。

这十步里没有“停掉共用服务”,也没有“重写所有站点”。若你发现自己想插入其中一步,就是范围在扩大。扩大可以是别的文章里的任务,不是这一张顺序里的下一步。

顺序可以打印出来放在旁边。做的时候用笔勾已完成的步,并在旁边写短行的位置。中断了,下一个接着的人从最后一步已勾的地方继续,而不是从第一步重做。重做往往会造成第二次重载。

换发的那天读者应该什么都感觉不到

成功的续期对读者是静默的。他不管从根域名还是从 www 来,握手都成功,文章都在,样式都在。他不会知道你昨夜替换了一对文件。这是目标,不是运气。静默来自几个具体的条件:新证书在旧证书过期前就已经在出示,两个名字都在新证书里,跳转没有改主机名,应用没有重启,旧域名也没有被换证书。

任何一个条件被打破,读者都会有感觉。过期才换,他在间隙里看见警告。新证书少了一个名字,那个名字的读者看见警告,另一个名字的读者没有感觉,于是你收到一半的投诉,容易误判成个别人的网络问题。跳转改了主机名,他的书签虽然还能开,地址栏却变了,他会以为被带到了别处。应用重启,他可能赶上一次短暂的错误页。旧域名被波及,投诉来自你完全不认识的读者,他们访问的是另一个站点。

所以换发日的验收不是仪式上的刷新,而是在证明“没有人必须有感觉”。短行全部与上一列对齐,就是这个证明。有一行对不齐,就还没有结束。不要在对不齐的时候发公告说升级完成。公告代替不了那一行。

读者若写信说看见警告,向他要主机名和警告的第一句。用这两者决定进哪一层,而不是请他清缓存作为第一反应。清缓存只在你已经用另一个客户端证明服务器是对的之后,才是合理的建议。服务器是错的时候,请他清缓存是在把他支开。按层修好,再用短行证明,然后才告诉他可以重试。

静默也意味着你不在页面上宣布“本站已更新证书”。读者不需要这个信息。证书是入口的事。页面上的公告会让人以为内容有变化,引来一批无谓的刷新。内容若真的有变化,那是发布日的事,用内容自己的方式说明。不要把两个日子的语言混在一起。

回退只动你刚刚动过的那一层

回退的范围必须和进入时一样窄。解析改错了,把记录改回去,等生存时间,不要去动证书。证书文件确认失败,不要替换,正式路径上仍是旧的一对,入口不需要回退,因为它还没被重载。替换之后测试失败,把上一对放回正式路径,再测试。测试通过而验收短行失败,按失败的那一行回退:跳转写错就改回跳转,不要把证书也换成旧的。证书若是好的,留下它。

唯一需要入口再重载的回退,是你已经重载过、而新文件或新规则是错的。这时把错误的那一处改回,测试,再平滑重载一次。不要重启机器来“彻底回到今天早上”。彻底的回退会把同一天里别人的正确变更也一起带回早上。你的笔记若只记了自己的层,就只回退自己的层。

应用的基准地址写错导致循环时,回退应用的那一项设置即可。入口的“保留主机名”可以保持。两边同时回退,循环没了,你却不知道哪一边才是你要的策略。卡片上若写着两个名字都停留,入口的规则就没错,错的是应用。只改应用。改完用登录页和一篇文章的链接各看一次,确认地址栏不再交替。

回退之后把短行再记一列,标成回退。不要删掉失败的那一列。失败的列说明你曾经走到哪里。删掉它,下次会有人重蹈,因为看起来这条路从未失败。保留失败列,不等于保留坏文件在正式路径上。坏文件必须离开正式路径。记录留下,文件挪走。

若回退需要动到旧域名的任何配置,先停下。说明你的变更已经越过边界。越过边界的回退要和旧域名的负责人一起看,而不是按你的笔记单独改回去。你以为的“改回去”可能覆盖他在同一天做的合法修改。这是另一个理由,让你的文件和他的文件从一开始就分开。

练习之后才碰到真实名字

真实名字的第一次申请,应当看起来像练习的重复,而不是像一场即兴。卡片已经写好,探针已经从外部成功,授权查询已经是空的成功或明确的允许,顺序表的前三步已经勾过。真实申请只是把假名字换成真实名字,把自签换成机构签发。

真实名字会有真实的缓存。练习时你的对照文件没有生存时间的问题。真实记录有。所以在真实的第二步里多出来的,只是等待。等待不是新的技术。不要在等待时开始改入口来打发时间。等待时可以写短行的表头,可以确认验证目录不在发布范围内,可以通知旧站点的负责人窗口。这些都不改变线上行为。

真实签发得到的文件,按第六步确认后再替换。确认的项目和生产练习时一样:两个名字、日期、链条、密钥匹配。练习时你见过自签的名字清单长什么样,所以这里的清单不会让你陌生。陌生的只是签发者变成了你信任的机构。这正是你要的变化。

第一次真实重载之前,把旧域名的底片记下来。练习里没有旧域名。生产里有。底片是生产多出来的责任。没有底片就不要重载。重载之后立刻按底片再请求一次。对不齐就回退入口配置,而不是继续做后面的内容验收。旧域名对不齐时,新站点即便全好,这次变更也不算成功。

真实的基准地址一旦写进站点地图并被抓取,再改就会让外部的索引变来变去。所以卡片上的基准地址在第一次发布地图之前就要定下来。证书可以以后每九十天静默替换。基准地址不应每九十天思考一次。它是策略,不是续期的产物。

以后加第三个名字时不要撕开这一对

某天你可能需要 blog.example.com 这样的第三个名字。不要把它追加进一次慌忙的续期里,也不要为了它停掉现有服务。第三个名字是一次新的变更:它需要自己的解析、自己的验证、自己的验收短行。你可以选择签发一张新的、包含三个名字的证书来替换这一对,也可以让第三个名字使用完全独立的一对文件和一段独立的站点规则。

选择替换这一对时,范围变了,就不再是“内容没变”的续期。它是一次新的签发。新的签发仍用同一个验证目录的方式,仍不停止共用服务。验收时除了原来的所有短行,还要加上第三个名字的四次协议请求。原来的短行必须仍然对齐。对不齐,说明三个名字的新证书或新规则波及了原来的两个。

选择独立的一对时,原来的这一对完全不动,定时任务也不改。第三个名字有自己的续期。入口会多一段规则。这样做的好处是原来的静默续期保持原样。代价是入口上多一对文件要记得。两种选择都可以。不可以的是在没有选择的情况下,让续期脚本每次都“看看有没有新名字”。脚本的名字清单应当是明确的两个,和卡片一致。要加名字,先改卡片,再改脚本。

第三个名字未就绪时,不要先把它写进证书申请。写进去就会触发对它的验证。验证失败,原来两个名字的换发也会一起失败。你可能因此错过原来那一对的续期窗口。这是范围扩大最现实的风险:不是哲学上的不整洁,而是旧的一对被新的名字拖过期。

在第三个名字的变更完成之前,这篇的定时任务继续只认识原来的两个。任务的简单是它能在夜里静默的原因。不要为了未来的灵活性把任务写成一个会扫描所有站点的程序。扫描所有站点,就回到了“惊动全部”的路上。

你要能向别人讲清这三层

做完之后,你应该能用很短的话向另一个维护者讲清,而不必打开机器。解析让两个名字从公网到达这台机器。证书让这两个名字出现在同一张叶子上,由现有的八十端口提供验证文件,不停邻居。页面让未加密请求留在当前主机名的加密地址上,绝对链接另选一个基准地址。续期只替换这一对文件。摘要没变,就不重载入口。验收分别请求两个主机名的两种协议、一篇正文、一个静态资源,以及一个旧域名。

他若只能记住一句,就记住:名字不在证书里,跳转还没有机会发生。这句话能阻止最常见的省略,也就是只签一个名字,指望另一个被跳转收走。第二句值得记住的是:查询授权失败,不等于没有授权记录。这句话能阻止在域名服务故障时去停网站。第三句是:文件没变,就不要重载全部站点。这句话能阻止每夜的仪式。

三句话都对应短行里的某一行。讲的时候指着那一行,而不是指着记忆。记忆会把空的成功和服务器失败混成“好像没有记录”。短行不会。

若他准备改策略,让其中一个名字变成跳板,请他先改卡片,再改跳转,并保持证书清单不动,除非他有明确理由去掉一个名字。去掉名字也是一次新的签发,不是编辑旧文件。旧文件里的名字删不掉。你只能签发一张没有那个名字的新证书来替换。替换前要想清楚旧链接。旧链接会在握手阶段就失败。这通常不值得。保留两个名字,即使其中一个只是跳板,成本只是清单里多一行,验证时多取一次文件。一次文件换来旧链接不被警告页拦住,这是合算的。

讲完之后让他从外部做一次未加密的 www 请求,看目标主机名。他能看懂这一行,就掌握了这篇最容易配错的地方。其余的层可以在出了相应的故障时再读。文章的长度是为了在那时有地方可查,不是为了在第一天全部背下来。

收束:一对名字,一对文件,一行行的证据

根域名和 www 是两条入口,不是两个网站。它们可以共用一张证书、一个验证目录、一份页面。它们不必共用一个被写死的跳转目标。绝对链接需要一个基准地址,那是生成器的选择,不是读者的义务。

现有的八十端口已经能把验证文件交出去,就不要为了申请或续期把共用的门关上。邻居的旧域名用自己的短行证明自己没被波及。授权查询要能分清空的成功和失败。续期替换的是这一对文件。比较摘要,没变就离开,变了才在测试之后平滑重载入口。

这些事情都做完时,一个从旧邮件点开 www 的人,和一个记住根域名的人,会看见同一篇文章。地址栏里的名字是他们自己带来的那一个。加密是安静完成的。旁边那个旧站点的读者则什么都没注意到。这就是这次变更该有的样子。

见字如晤

图解