三个容器在跑,面板上三个站点建好了,反代规则补完,HTTPS 也启用了。
只有一件事没做:域名的 A 记录还指着旧机器。
不切 DNS,公网访问不到新机,没法验。切了 DNS,流量立刻过来。新机但凡哪儿不对,用户当场就看到一个坏站点。想退回去还得等 TTL 慢慢过期,那是解析记录在本地 DNS 上的缓存时间。
我一开始的做法很自然:先切,再盯着浏览器刷新,看它什么时候生效。这等于拿线上流量做验证。
那阵子我卡在一个误解里,以为「域名没解析过来」就等于「没法验证」。这两件事其实毫无关系。
服务器认的是 Host 头,不看 DNS。一台机器上能有几十个站点,它们共用同一个 80 端口,服务器靠 HTTP 请求头里的 Host 区分你要哪一个。只要请求到了本机的 80 端口,Host 又写着目标域名,面板就当这个域名的访问来了,按该站点的反代规则处理。这个域名在公网上解析到哪儿,它根本不看。
HTTPS 比 HTTP 多一层握手,这一层需要域名参与。建连接时先握手,服务器得挑一张证书递过去,客户端在握手最早期就把自己要连的域名明文报上来,这个字段叫 SNI,服务器靠它选证书。
所以验 HTTPS 光伪造 Host 头不够。连接得真的连到本机,还要让 curl 以为这个域名就在本机。--resolve 干的就是这件事:它只改这一条请求的解析结果,把域名指到 127.0.0.1,全局 DNS 不动,hosts 文件也不碰。这下 SNI 还是真实域名,证书对得上,连接却打到了本机 443。
弄清楚这两条,我手上的分工也就出来了。Host 头那条打本机 80,验的是路由规则对不对,站点建没建、location 生没生效,这一层不要证书。--resolve 那条打本机 443,只验 TLS 通不通、证书绑得对不对。
Host 头打本机 80:
bash
curl -s -o /dev/null -w "%{http_code}\n" -H 'Host: h5.example.com' http://127.0.0.1/
curl -s -o /dev/null -w "%{http_code}\n" -H 'Host: h5.example.com' http://127.0.0.1/api/v1/products
这组回显里有个反直觉的用法。有回在建站之前跑它,屏幕上出来的是:
/ → 200
/api/v1/products → 404
看到 200 我差点松口气,可这里的 200 是坏消息。
因为这个域名还没有站点,请求落到了面板的默认站上。默认站只有 / 能答,/api/... 一律 404。所以这组回显真正说明的是站点还没建,跟后端坏没坏没关系。
我当时是这么判读的:/ 是 200、/api/... 是 404,站点未建,请求落到了面板默认站;/ 是 200、/api/... 也是 200 并且带 JSON,站点已建,两条 location 都生效了;/ 是 000 或者连接被拒,面板 Web 层没在监听 80,先去查它;两个都非 200,站点建了但规则不对,回配置文件里看 location。
接口域名的根路径返回 404 是正常的,纯接口后端本来就没有 / 路由。要验它,得换成 /health 这类健康检查路径。
--resolve 打本机 443,验 TLS 和证书:
bash
for d in h5 api admin; do
printf "%-20s " "$d.example.com"
curl -skI --resolve $d.example.com:443:127.0.0.1 https://$d.example.com/ | head -1
done
我期望的是 HTTP/2 200。接口域名的 / 有可能是 404,但只要响应头是 HTTP/2,就说明 TLS 握手通了、证书也选对了。这一条验的是传输层,业务层得去 /health 上看。
-k 是拿来跳过证书校验的。跳过校验不等于不检查证书,真想看证书用的是这个:
bash
echo | openssl s_client -connect 127.0.0.1:443 -servername h5.example.com 2>/dev/null \
| openssl x509 -noout -subject -dates
subject 里应该是你的域名,dates 里是起止时间。
这台机器上除了自己的 3 个域名,还有 6 个别人的线上站点。每次动面板 Web 配置的前后,我各跑一次:
bash
for h in h5.example.com api.example.com admin.example.com \
<他人域名1> <他人域名2> <他人域名3> <他人域名4> <他人域名5> <他人域名6>; do
printf "%-24s " "$h"
curl -s -o /dev/null -w "%{http_code}\n" --max-time 8 -H "Host: $h" http://127.0.0.1/
done
任何一项从 200 或 301 掉成 4xx、5xx,我立即停手回滚,不想着先改完再说。
改 hosts 也能达到一样的效果,我不改,因为它有三个毛病:它是全局的,改完你在这台机器上看到的一切都被污染;它要 root;最麻烦的是你会忘了改回来,过几天对着一个「配置明明对却不生效」的怪象查半天,其实机器还在走 hosts。
curl -H 和 --resolve 都只活在单条请求里,命令跑完什么都没留下。
更要紧的是,命令本身就是证据。一条 curl 命令能直接贴进文档、贴进群聊,别人拿去就能重跑,回显是可以复现的。而「我改了 hosts 然后访问了一下」复现不了,没人知道你当时改了什么。
顺序我也讲究:先 Host 头,后 --resolve。
先跑 curl -H 'Host:' 打 80。它不要证书、不要 HTTPS 配好,一口气回答三个问题------站点建没建、location 生没生效、后端活着没。这一层不通,后面的 HTTPS 根本不用看。再跑 --resolve 打 443,这一层只回答 TLS 和证书对不对,前提是 HTTP 层已经通了。
反过来先看 TLS 会白费时间。TLS 报的错往往很含糊,证书名不匹配、握手失败,你会分不清是证书的事还是路由的事。
DNS 切换危险,不在于操作有多难,而在于它是第一个不可逆的动作。在它之前你随时能改回去,在它之后你要面对缓存、TTL、两地状态不一致。
有了这两条 curl,切换前的事实清单就能凑齐。
三个站点的路由规则对不对,我用 Host 头看。接口在域名层返回的是 JSON 还是 HTML,用 Host 头加 head -c 120 看响应体。TLS 通不通、证书绑没绑对站点,用 --resolve 看。强制跳转生没生效,80 能不能先跳 301 再到 https,用 Host 头加 grep -i '^location' 看。同机别人的站点受没受影响,跑一遍九站点基线。
切 DNS 那一刻,我已经知道结果了。
还有一条配套动作:切换前 24 小时,把 A 记录的 TTL 从默认的常见 600 秒降到 60 秒,让生效和回退都落在 1 分钟内。这一步很多人漏,它决定的正是出问题时你能不能快速退回去。
我照着这张单子过。
站点已建,curl -H 'Host: <域名>' 分别打 http://127.0.0.1/ 和一个后端专有路径,两个都该是 200。
前端接口分流对不对:
curl -s -H 'Host: h5.example.com' http://127.0.0.1/api/v1/products | head -c 120
返回的该是 JSON。接口域名健康不健康,curl -s -H 'Host: api.example.com' http://127.0.0.1/health,回 status:ok。
TLS 通没通,curl -skI --resolve <域名>:443:127.0.0.1 https://<域名>/,该回 HTTP/2。证书对不对得上,openssl s_client -connect 127.0.0.1:443 -servername <域名>,subject 里要有这个域名,日期得有效。
强制跳转在不在:
curl -sI -H 'Host: <域名>' http://127.0.0.1/ | grep -i '^location'
该指到 https://<域名>/。
九站点基线就是前面那条 for 循环,我改配置前后各跑一次,9 项都要和改前一致。TTL 降没降,提前 24 小时在解析控制台改成 60 秒。回退方案就绪没,能不能在 1 分钟内把 A 记录改回旧机器,这一点得确认过。
服务器看 Host 头,不看 DNS,请求能到本机 80,域名解析在哪儿都无所谓。--resolve 只改这一条请求的解析,SNI 还是真域名,所以验 HTTPS 不用动全局。/ 是 200 而业务路径 404,先怀疑站点没建、请求落到了面板默认站,别先怪后端。
验完再切。