网站加载慢怎么解决?CDN 加速与缓存策略实操指南

网站加载慢怎么解决?先用 TTFB 定位瓶颈,再决定 CDN、Cache-Control 与 TTL 怎么配,附压缩、图片格式与 HTTP/2 的真实收益和测量方法。

2026-09-12

网站加载慢怎么解决?先用 TTFB 定位瓶颈,再决定 CDN、Cache-Control 与 TTL 怎么配,附压缩、图片格式与 HTTP/2 的真实收益和测量方法。

「网站加载慢」是最常见、也最容易被误诊的问题。慢可能出在 DNS 解析、TTFB、图片体积、JavaScript 执行,也可能只是缓存头没配对。这篇按真实诊断顺序展开:先用命令定位瓶颈,再决定要不要上 CDN、缓存设多久、图片怎么压——而不是先买 CDN 再猜哪里慢。

判断顺序:先看 TTFB,再看传输体积,最后看渲染阻塞。CDN 只能解决前两项里的一部分,第三项(JS 执行、重排)必须靠代码和构建侧优化。

先定位瓶颈:加载慢到底慢在哪一段

把一次页面加载拆成四段,问题通常就现形了:连接建立(DNS + TCP + TLS)、等待首字节(TTFB)、传输(下载 HTML / CSS / JS / 图片)、解析与渲染(JS 执行、布局、绘制)。四段的优化手段完全不同,用错工具等于白花钱。

  • TTFB > 600ms:瓶颈在后端或区域。优先考虑 CDN + 换更近的源站区域
  • TTFB < 200ms 但首屏慢:瓶颈在资源体积与阻塞,先压图片、合并请求
  • 第二次访问快、第一次慢:缓存已经生效,重点优化首访(预热、关键 CSS 内联)

定位只需一条命令,四个数字就能分清是谁的问题:

curl -s -o /dev/null -w "dns:%{time_namelookup}s tcp:%{time_connect}s ttfb:%{time_starttransfer}s total:%{time_total}s\n" https://example.com/ time_starttransfer 就是 TTFB。再加 -H 'Cache-Control: no-cache' 重测一次:如果 TTFB 明显升高,说明之前命中的是 CDN 缓存而不是源站的真实响应速度。

CDN 实际做了什么:边缘节点、反向代理与缓存命中

CDN 本质是放在用户旁边的一层反向代理加缓存。它把可缓存内容缓存在各主要城市、各大运营商出口的边缘节点(PoP)上,用户就近取数据,而不是每次都回源站。节点覆盖、计费口径与回源策略怎么比,见 CDN 加速。

  • 延迟降低:用户到边缘节点通常 5–30ms,替代掉回源的 100–200ms 跨境往返
  • 缓存命中:命中时完全不回源,实务中静态资源的 CDN 命中率普遍在 80%–95%
  • 源站减压:带宽与连接数压力转移到 CDN,源站只需保留兜底带宽
  • 协议优化:多数 CDN 默认支持 HTTP/2、HTTP/3 与 TLS 1.3,可统一开启

怎么确认是否命中:看响应头。Cloudflare 是 cf-cache-status: HIT,AWS CloudFront 是 x-cache: Hit from cloudfront,Fastly 是 X-Cache: HIT,阿里云 DCDN / 腾讯云 CDN 常见为 X-Cache。字段名各家不同,先 curl -sI https://你的域名/ 看一眼再判断。

首次访问未命中时,CDN 可能比直连源站还慢一点,因为多了一跳边缘节点。所以规则要配对:静态资源长缓存,HTML 短缓存。HTML 被长期缓存才是 CDN 上线后最常见的翻车点。

TTFB 决定首屏上限,也直接决定转化

TTFB(Time To First Byte)是浏览器发出请求到收到第一个字节的时间。它决定了 HTML 什么时候开始下载,也基本决定了 LCP 的下限——实测中 TTFB 常占 LCP 的一半以上。Google 的参考线是 TTFB ≤ 800ms 为良好,Core Web Vitals 要求 LCP ≤ 2.5s,超出后留给你渲染的时间会被直接吃掉。

更现实的问题是营收。Google 与 SOASTA 的大型零售性能研究里,移动端页面速度每下降 1%,销售额下降约 1%;在人为提速的 A/A 实验中,首屏快 20% 对应转化率提升约 9%。这类数字来自特定样本,别当成自己的预期值,但方向和量级足够说明:TTFB 是营收项,不是技术洁癖。

可落地的目标与手段:

  • CDN 命中场景,把 TTFB 稳定控制在 200ms 以内
  • 源站区域选在用户附近,跨境往返每省 100ms,用户感知就是「快了一档」
  • 用页面级缓存或反向代理缓存,绕开重复的数据库查询
  • 把首屏不需要的模块(推荐位、评论、第三方脚本)改为异步加载

判断是否真的变快了:CDN 上线后 TTFB 从 150ms 掉到 30ms,但总时长没变,说明瓶颈已经转移到传输体积或渲染——这时继续调缓存是浪费时间,应该转向图片与 JS。

Cache-Control:把缓存策略写进响应头

浏览器和 CDN 都只认响应头。缓存策略不写在 Cache-Control 里,默认行为就是「每次重新验证」,等于没开缓存。下面四组是最常用的写法。

带内容 hash 的 JS / CSS Cache-Control: public, max-age=31536000, immutable HTML 页面 Cache-Control: public, s-maxage=60, stale-while-revalidate=300 带版本号的图片 Cache-Control: public, max-age=2592000 登录后的页面 / 个人数据 Cache-Control: private, no-store

  • max-age:浏览器缓存秒数;s-maxage:共享缓存(CDN)秒数,优先于 max-age
  • no-cache ≠ 不缓存:允许存储,但每次必须回源校验(可用 304,不传 body)
  • no-store:完全不写缓存,个人数据只该用这个
  • immutable:刷新页面也不重新校验,只对文件 URL 带 hash 时安全
  • stale-while-revalidate:过期后先返回旧内容再后台更新,HTML 强烈建议加

压缩要生效,必须带上 Vary: Accept-Encoding,否则 CDN 可能把 brotli 版本发给不支持的客户端,或反过来发 gzip 版本。

TTL 怎么定:静态与动态内容的差别

TTL 的核心取舍只有两个:更新延迟(内容多久能生效)与回源率(源站承受多少请求)。静态资源把 TTL 拉长、更新交给文件名 hash;动态内容用短 TTL 换新鲜度。把静态资源从应用服务器搬到对象存储再挂 CDN,是这一步最省事的做法——存储分层与生命周期规则见 AWS S3 存储分层。

内容类型建议 Cache-ControlTTL 参考说明
带 hash 的 JS / CSSpublic, max-age=31536000, immutable1 年改内容就换文件名,可放心长缓存
带版本号的图片 / 字体public, max-age=259200030 天换 URL 或改文件名触发更新
HTML 页面(文章、首页)public, s-maxage=60, stale-while-revalidate=30060 秒 – 5 分钟发版后基本立即可见
GET 型公开 API(列表、行情)public, s-maxage=5-305–30 秒按业务可接受的陈旧度定
带鉴权的 API / 购物车 / 订单private, no-store不缓存任何公共缓存命中都是数据泄露
已登录页面(HTML)private, no-cache每次校验避免 A 用户看到 B 用户内容
第三方脚本与字体按供应商建议—优先本地托管,别把核心渲染压在第三方上

动态内容从 5–30 秒起步,而不是直接 0:先观察业务真实更新频率,如果某接口一天只改几次,TTL 放到 60 秒也没问题,回源率能降一个量级。

浏览器缓存、CDN 缓存与源站缓存的分工

一次请求会依次经过三层,能在上一层解决就不要往下传。三层各自独立配置,失效逻辑也不同。

  1. 浏览器缓存:受 max-age 控制,二次访问零网络请求(DevTools 里显示 memory cache / disk cache)
  2. CDN 缓存:受 s-maxage 控制,命中后不回源,是节省源站带宽的主力
  3. 源站缓存(Nginx / 应用层页面缓存):CDN 未命中时的最后一道防线,直接决定 TTFB

当 CDN 不缓存或需要刷新时,浏览器与源站之间靠协商缓存:ETag(或 Last-Modified)配合请求头 If-None-Match,内容没变就回 304 Not Modified,不传 body。协商缓存省的是带宽和时间,不会减少请求次数。

别把 HTML 设成长期缓存。 页面结构变了但用户拿到的是旧 HTML,是比慢更严重的问题——发版后功能异常、链接 404。HTML 的 TTL 应该短到能被业务接受。

压缩与图片:省下来的都是传输时间

同等内容下,传输时间正比于字节数。10Mbps 带宽传 1MB 需要约 0.8 秒,传 300KB 只需要约 0.25 秒——这不是理论值,直接乘进首屏时间。

  • 文本压缩:HTML / CSS / JS 开 gzip 通常减少 60%–80%;brotli 在同等质量下再比 gzip 小约 15%–25%
  • 图片格式:WebP 相对 JPEG 常见节省 25%–34%,AVIF 相对 JPEG 常见节省约 50%
  • 按屏幕发图:用 srcset + sizes 给 2 倍屏用户发 2 倍图,避免所有设备都下载大图
  • 延迟加载:首屏以下的图片加 loading="lazy",首屏主图不要加
  • 预留尺寸:图片写明 width / height,把 CLS 压到 0.1 以内

LCP 图要特殊对待:加 fetchpriority="high" 或 <link rel="preload" as="image"> 提前发起请求,并且排除 lazy load。它是首屏最大的资源,抢不到请求槽位,压缩收益再高也没用。

多数 CDN 支持按 Accept 请求头自动转换 WebP / AVIF,并在边缘完成 brotli 压缩。启用前确认两件事:厂商是否支持,以及转换后是否仍走同一套缓存键(否则一个 URL 会缓存两份格式,命中率白白下降)。

HTTP/2 与 HTTP/3:连接复用到底值多少

一个包含 40 个静态资源的页面,在 HTTP/1.1 下通常要开 6 个连接、经历多次 TCP 与 TLS 握手。HTTP/2 用单连接多路复用加 HPACK 头部压缩,一次握手就能并发请求,实测一般能省 100–300ms 的首屏时间。

  • CDN 与源站都要开 H2。只在 CDN 开,边缘到源站那段仍走 HTTP/1.1,收益减半
  • 不要再做「合并成一个大文件」「跨域分片域名」——这是 HTTP/1.1 时代的解法,在 H2 下反而拖慢解析
  • HTTP/3 基于 UDP / QUIC:支持 0-RTT 恢复与更好的丢包恢复,移动网络切换时体验提升明显

HTTP/3 要灰度再上。 部分企业防火墙、老旧运营商网络与中间设备会阻断 UDP。可用 curl --http3 -s -o /dev/null -w "%{http_version} %{time_starttransfer}s\n" https://example.com/ 验证协商是否成功,失败时应自动回落到 HTTP/2。

内容变了,缓存怎么失效

长缓存带来长 TTL,也就带来「改了内容用户看不到」的问题。按可靠性排序,三种方案优先级不同。

  1. 版本化 URL(首选):文件名或查询串带内容 hash,如 app.4f3a2b.js。新内容天然是新 URL,不需要任何刷新动作,CDN 命中率不受影响
  2. 按 URL / 目录刷新:内容必须保持同一 URL 时才用。发布流水线里发一次刷新请求,通常几秒到几十秒内全网生效
  3. 预热:发布后主动回源拉取新版本(curl 预热接口或控制台预热),避免冷启动时大量用户同时穿透到源站

三种方案的生效时间:版本化 URL 首次访问即生效(无需任何操作);单 URL 刷新通常几秒到几十秒内全网生效,接口级刷新接口限速的差异较大;预热则是主动提前拉取,成本最低但需要你在流水线里多写一步。

注意刷新本身有成本:全量刷新会造成回源风暴,大流量站点应在低峰期执行,或配置刷新限速与自动预热。刷新后立即用 curl -sI 复验一次,确认拿到的是新版本再继续发布后续变更。

怎么测量:Lighthouse、WebPageTest 与 TTFB 基线

优化必须先有基线。没有改前改后的对比,任何性能结论都是猜测。三个工具各管一件事:

  • Lighthouse(Chrome DevTools → Lighthouse,或 npx lighthouse https://example.com --only-categories=performance):给 LCP、CLS、TBT 与「减少初始服务器响应时间」审计项
  • WebPageTest:多地节点对比 TTFB、First Contentful Paint,并区分 First View 与 Repeat View,直接看出 CDN 是否生效
  • DevTools Network 瀑布图:看每条请求的 Size 列、Status 200 / 304、Priority,以及 memory cache / disk cache 命中标记
指标良好偏差工具
TTFB≤ 0.8s(CDN 命中建议 ≤ 0.2s)> 1.8sWebPageTest / curl
LCP≤ 2.5s> 4.0sLighthouse / CrUX
INP≤ 200ms> 500msLighthouse / 真实用户监控
CLS≤ 0.1> 0.25Lighthouse

实验室数据不等于真实体验。 办公室网络通常又快又稳,测出来一切都好。条件允许时接入 RUM(真实用户监控)或至少用 WebPageTest 多地节点复核,避免优化只对测速工具有效。

一周可执行的优化清单

按这个顺序做,一周内就能看到 TTFB 和首屏的明确变化。每一步都有可验证的数字。

  1. 第 1 天 · 建基线:用上面的 curl 命令记录 DNS / TCP / TTFB / 总时长,再跑一次 Lighthouse,把四项数据存档
  2. 第 2 天 · 分层诊断:找出 TTFB > 600ms 的接口,和体积最大的 5 个资源,其余先不动
  3. 第 3 天 · 缓存头:给带 hash 的 JS / CSS 设一年 immutable,HTML 设 s-maxage=60 + stale-while-revalidate,确认 Vary: Accept-Encoding
  4. 第 4 天 · 上 CDN:先只接管静态资源,验证 X-Cache 命中头与 TTFB 是否降到 200ms 以内,再考虑接入 HTML
  5. 第 5 天 · 压缩与图片:开启 brotli,WebP / AVIF 自动转换,首屏图加 fetchpriority="high" 并去掉 lazy load
  6. 第 6–7 天 · 协议与复核:确认 CDN 与源站都开了 HTTP/2,灰度验证 HTTP/3,重跑 Lighthouse 与 WebPageTest 对比基线

下一步:先只做前三步(建基线 + 改缓存头),这一步不需要采购新资源,却通常能砍掉相当一部分重复请求。如果 TTFB 仍然偏高,再去评估 CDN 与更近的源站区域——把上面 curl 命令测出的三个数字(TTFB / 总时长 / 是否命中缓存)整理成一张表,拿去比方案会高效得多。

按自己的用量算一遍?

告诉我们区域和配置,工程师给出配置建议与报价;支持 USDT TRC-20 付款。

立即充值