← 返回首页
Article

静态博客性能优化

更新于 2026年6月21日 8 个章节

最近看自己的静态博客时,我发现一个很典型的问题:页面本身打开还行,但一滚到带图的文章,加载就开始拖。

这和源站带宽关系很大。个人博客常用的小带宽云服务器,跑 HTML 没什么压力,一遇到高清截图和架构图就吃紧。图片稍微大一点,浏览器就开始一张张等。

所以这次我不想只把问题归到“服务器太小”。静态博客的性能优化,可以按一个更实际的顺序来做:先让图片变小,再让图片从更近的地方读,最后处理缓存更新带来的副作用。

这篇文章就按这个顺序记录。场景很普通:内容写在 Markdown 里,构建成静态站点后部署到云服务器,Nginx 对外提供 HTML、CSS、JS 和图片。

先看慢在哪里

一个静态博客的发布链路通常很朴素:

  1. Markdown 写在内容仓库里。
  2. 构建时把文章和图片复制到静态站点目录。
  3. Nginx 或其他 Web Server 对外提供 HTML、CSS、JS 和图片。
  4. 用户访问文章时,图片也从这台源站服务器读取。

这个模型没问题,甚至很适合个人站点。麻烦出在图片请求上。

HTML 文档可能只有几十 KB,一篇文章里的截图却可能轻松到几 MB。读者离源站远一点,首包和下载时间都会变长;同一篇文章被集中转发,源站带宽也会被图片流量吃掉。最后表现出来就是:页面骨架出来了,正文也出来了,图片还在一张张等。

要解决这个问题,我会先做两件事:减少每张图的体积,减少每次访问都打到源站的概率。

静态博客 CDN 架构图

第一步:把图片压小

图片压缩是最便宜的一步。

CDN 解决的是“从哪里读”的问题,图片压缩解决的是“读多少”的问题。如果一张截图原来有 8MB,哪怕后面接了 CDN,第一次回源、边缘缓存、用户下载都还是围着这 8MB 转。先把它压到几百 KB,再交给 CDN 分发,源站压力和用户等待时间都会一起降下来。

我更倾向在构建阶段自动处理,少做手动压图。原始图片继续保留在内容仓库里,方便以后重新生成;发布产物里只放适合网页展示的版本。比如可以按这个策略做:

  1. png/jpg/jpeg 统一转成 WebP。
  2. 最大宽度限制在 1600px 左右,小图不放大。
  3. WebP 质量先设在 80-85 之间,博客截图和说明图通常够用。
  4. gif/svg/webp 先原样保留,避免动图丢失或 SVG 被错误栅格化。
  5. Markdown 里仍然按原来的图片路径写,构建时再把最终引用改成 .webp

这样写文章的时候不用多一步手工处理,发布时也不会把原始大图直接丢到服务器上。这次我把已有构建产物里的 18 张图片做了一轮 WebP 转换,原始体积大约 74.6MB,输出后约 2.1MB。这个数字只代表这次站点里的图片情况,但足够说明个人博客里“先压图”有多划算。

还有一个小细节:不要为了省事去覆盖原图文件。原图是素材,WebP 是发布产物,两者分开,后续要调整质量、尺寸或换格式时会轻松很多。

第二步:给整站接 CDN

图片变小以后,再给站点接 CDN。

对个人博客来说,第一步可以很简单:直接把博客主域名接入 CDN。假设博客域名是 blog.example.com,源站是一台云服务器,或者有一个只给回源使用的 origin.example.com,配置思路大概是这样:

  1. 在 CDN 控制台添加加速域名 blog.example.com
  2. 业务类型选择“网页小文件”,静态博客基本就是这类资源。
  3. 源站填写服务器公网 IP,或者填写 origin.example.com
  4. 回源协议优先用 HTTPS;源站暂时没配证书时,也可以先 HTTP 回源。
  5. 给 CDN 域名配置 HTTPS 证书。
  6. DNS 里把 blog.example.com 从原来的 A 记录切到 CDN 分配的 CNAME。

这里我会尽量把“用户访问域名”和“CDN 回源地址”分开。blog.example.com 给读者访问,origin.example.com 只给 CDN 回源用。这样出问题时也好判断:访问 CDN 有问题,还是源站本身已经拿不到文件。

如果只是为了让图片快一点,不需要一开始就把图片迁到对象存储。整站 CDN 的改造成本低,文章 Markdown、构建脚本、图片引用路径基本都不用改,收益却很直接。

第三步:缓存规则别一刀切

CDN 接上以后,后面体验好不好,主要看缓存规则。

静态博客里的资源更新频率差异很大。HTML 变动比较敏感,首页、归档页、文章页都可能因为一次发布发生变化;图片则稳定得多,尤其是文章里已经发布过的截图和架构图,通常很少覆盖。

我会按资源类型拆开配置:

资源推荐缓存原因
HTML不缓存,或 1-5 分钟首页和文章更新后要尽快可见
图片目录,例如 /uploads/posts/30 天或更长图片最占流量,缓存收益最大
图片后缀,例如 webp/jpg/png/gif/svg/avif30 天或更长减少重复回源
带 hash 的 CSS/JS30 天到 1 年文件名变化已经代表版本变化
RSS、sitemap5-30 分钟更新不频繁,但没必要长时间缓存

这里最值得改的是图片发布习惯:如果图片内容变了,尽量改文件名,不要覆盖原文件。

比如 architecture.png 已经被 CDN 缓存了,你后来直接覆盖同名文件,就要考虑 CDN 刷新、浏览器缓存和排查成本。把它改成 architecture-v2.png,问题会少很多。文件名变化以后,新文章引用的是新地址,旧缓存继续留着也不会影响读者看到新图。

源站侧可以补一层响应头,作为默认兜底:

location /uploads/posts/ {
  add_header Cache-Control "public, max-age=2592000";
}

location ~* \.html$ {
  add_header Cache-Control "no-cache";
}

不过这段配置不用照抄。很多时候直接在 CDN 控制台按目录和后缀配置就够了。只有当你希望“CDN 遵循源站 Cache-Control”时,源站响应头才会变得更关键。

第四步:发布后再刷新缓存

CDN 会让读取变快,也会带来一个新问题:旧内容可能还在边缘节点上。

我会把发布分成两种情况:

  1. 新增文章、新增图片:通常不需要刷新,第一次访问时 CDN 会自动回源。
  2. 覆盖同名文件:需要刷新对应 URL,必要时刷新那篇文章的图片目录。

刷新动作要放在部署完成之后。顺序反过来没有意义:如果先刷新,CDN 下一次回源时源站还是旧文件,边缘节点只会重新缓存旧内容。

静态博客发布与 CDN 刷新流程图

一个比较稳的发布顺序是:

  1. 构建静态站点。
  2. 上传到服务器目录。
  3. 验证源站文件已经能访问。
  4. 对覆盖过的 HTML 或图片 URL 做 CDN 刷新。
  5. 对关键页面和大图做预热。
  6. 从公网域名再访问一次。

个人博客偶尔全站刷新问题不大,但我不建议把它当成默认动作。全站刷新会让大量请求重新回源,访问量上来以后反而给源站制造压力。能按路径刷新,就尽量按路径刷新。

图片很多以后,再考虑 COS

整站 CDN 的限制也很清楚:源站还是那台云服务器,只是大部分读取被 CDN 缓存挡住了。

当图片数量明显变多,或者图片流量已经远高于页面流量时,可以把图片单独拆到对象存储。腾讯云上常见的做法是 COS + CDN:

  1. 建一个 COS 存储桶,专门放博客图片。
  2. 绑定 img.example.com 这样的图片域名,并开启 CDN 加速。
  3. 构建或部署时,把图片同步到 COS。
  4. 让文章渲染后的图片地址变成 https://img.example.com/posts/...
  5. 主站继续部署 HTML,图片流量交给 COS + CDN。

我不会在一开始就做这一步。它会引入对象存储权限、跨域、图片同步、URL 重写、缓存刷新、费用拆分等一串新问题。博客规模还小的时候,整站 CDN 已经能解决最明显的慢。

等到图片真的成了主要流量,再把它拆出来,收益和复杂度才匹配。

我的优化顺序

如果现在从零给一个个人技术博客做性能优化,我会按这个顺序做:

  1. 先压缩图片,能用 WebP 就用 WebP,截图不要超过文章展示需要的尺寸。
  2. 主站接入 CDN。
  3. 图片目录和图片后缀设 30 天以上缓存。
  4. HTML 保持短缓存,避免文章更新后读者一直看到旧页面。
  5. 发布脚本里只刷新变更路径。
  6. 图片规模上来后,再迁到 COS + CDN。

这个顺序的好处是每一步都能单独验证。先看构建产物大小有没有降下来,再看图片响应头和 CDN 命中率,最后看源站带宽有没有下降。

我最不建议的是一上来就把问题做重。图片慢,先让图片变小;源站压力大,再让读者从更近的 CDN 节点读;图片真的成了主要流量,再看要不要拆对象存储。这个顺序通常比直接升级云服务器更划算。

参考资料