起因很朴素:ITDOG 全国 320 个节点测速,平均加载 7.99 秒。不是偶尔抽风,是每测一次都这个数。
一开始以为是自己页面太重——毕竟首页有两三兆。后来按顺序查下来,真正的元凶排在前面的其实是另外两个。
第一步:先看 DNS,别急着改代码
很多「站点很慢」的排查会直接从图片压缩、JS 优化开始,但如果访问路径本身就是绕的,前端怎么优化都是白费。
dig +short im.example.com
dig +short @arvin.dnspod.net im.example.com A
第一件事是确认域名解析到哪。我当时看到的是 CNAME → Cloudflare,而国内访问 Cloudflare 国际节点,延迟本身就摆在那。
问题在于:域名托管在 DNSPod,本来配了「境内 → 优化线路 IP」的分线路解析,但同名的 CNAME 还在。DNS 规范不允许 CNAME 与 A 记录在同一主机名下共存,而 CNAME 优先,所以那条境内 A 记录从头到尾没被用过。
删掉 CNAME 之后,解析立刻变成:
国内 ECS → 香港优化线路
境外 ECS → Cloudflare
这一步做完,路径是对的,但速度几乎没变。因为第二个问题浮出来了。
第二步:缓存没生效,要改在反代上
香港那台反代服务器(openresty)配置里,proxy_cache_path 一直都写着,但没有任何 location 引用它。缓存目录从建站到现在一直是 4K,一个字节都没存过。
同时 gzip_proxied 被限制成了特定缓存头才压缩,Halo 返回 Cache-Control: no-cache 的页面全都没压缩——89 KB 的 HTML 原样发出去。
改法就三处:
proxy_cache proxy_cache_panel;
proxy_ignore_headers Cache-Control Expires Set-Cookie;
gzip_proxied any;
gzip_types text/css application/javascript application/json
image/svg+xml text/plain application/xml;
效果:首页 TTFB 从 300–780ms 掉到 12–22ms,HTML 从 88 KB 压到 15.7 KB。
一个容易忽略的细节:只改源站的 Cache-Control 没用,如果响应还带 Set-Cookie,Cloudflare 会直接判定不可缓存(cf-cache-status: BYPASS)。我这边的情况是 CF 侧注入了一个访客统计 cookie,导致所有静态资源都没进边缘缓存。
第三步:再压体积
路径和缓存都对之后,才开始做页面本身:
- 首页背景视频 2.0 MB → 274 KB(720p30、CRF 26)
- 封面 PNG → WebP,单张 300 KB 级降到 50 KB 级
- 网站图标 98 KB → 7 KB
- 中文字体只保留导航用到的 188 个字符,335 KB → 46 KB
其中最容易踩的一点:替换了图片文件不等于访客能拿到新图。Halo 会为 ?width=800 这类参数生成派生图,缓存目录在 data/attachments/thumbnails/w800/,不删掉它就一直发旧文件;Cloudflare 那边还有一层,要单独清。
结果
ITDOG 320 节点平均 7.99s → 0.688s
首页传输体积 6519 KB → 1878 KB(-71%)
香港反代 TTFB 300–780ms → 12–22ms
还剩下的坑
- 字体分片浪费:主题自带的手写体切成 28 个分片,每个 58 KB,首页只用到 37 个字却拉下来 10 个分片(约 650 KB)。因为
font-display: swap,它不阻塞首屏,所以暂时留着。 - CF 的 Set-Cookie:只要那个统计 cookie 还在,境外访客就享受不到边缘缓存。国内走香港不受影响,优先级低。
- HTML 只缓存 60 秒:过期时会同步回源一次,约 200ms。加
proxy_cache_background_update可以消掉,属于锦上添花。
一点体会
这次最耗时的不是改配置,而是确认每一层到底有没有生效。
DNS 改了但 CNAME 没删,等于没改;缓存池配了但没被引用,等于没配;图片替换了但派生缓存还在,等于没换。每一次都要用 dig、响应头、x-cache-status 去验证,不能看着配置文件里的字就以为生效了。