<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Cloudflare on 小贝塔</title><link>https://blog.xbta.cc/tags/cloudflare/</link><description>Recent content in Cloudflare on 小贝塔</description><generator>Hugo</generator><language>zh-cn</language><copyright>2026 小贝塔</copyright><lastBuildDate>Fri, 30 Oct 2026 13:40:00 +0800</lastBuildDate><atom:link href="https://blog.xbta.cc/tags/cloudflare/index.xml" rel="self" type="application/rss+xml"/><item><title>把博客从 7 秒压到 1 秒：一次重建复盘</title><link>https://blog.xbta.cc/20261030-01/</link><pubDate>Fri, 30 Oct 2026 13:40:00 +0800</pubDate><guid>https://blog.xbta.cc/20261030-01/</guid><description>老博客打开要 7 秒，其中 5 秒全耗在 8 个外部 CDN 上。这篇记录我怎么把产物仓库改成源码工程、换掉主题、把 14MB 图片压到 1.5MB，以及最后那个绕不过去的天花板。</description><content:encoded><![CDATA[<p>这个博客的第一版是 2020 年搭的，Hugo + LoveIt 主题，写完直接 <code>hugo</code> 生成一堆 HTML 推到 GitHub 仓库，再接 Cloudflare Pages。能用，但一直有个心结：<strong>打开很慢</strong>。</p>
<p>这次索性推倒重来。结论先行：首页从 <strong>7.06 秒 → 1.1 秒</strong>，外部请求从 <strong>8 个 → 0 个</strong>，页面体积从 200 多 KB → <strong>28 KB</strong>。下面是完整过程，包括踩的坑。</p>
<img src="https://img.xbta.cc/blog/2026/20261030-01-01.webp" alt="重建后的博客首页" width="1440" height="1100" loading="eager" decoding="async" style="border:1px solid #e5e5e5;border-radius:6px">
<h2 id="一先体检别急着换工具">一、先体检，别急着换工具</h2>
<p>动手前我做了一件最该做的事：<strong>量化</strong>。</p>
<p>用 curl 把首页所有资源逐个打了一遍，结果很刺眼：</p>
<table>
	<thead>
			<tr>
					<th>资源</th>
					<th>来源</th>
					<th>耗时</th>
					<th>大小</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>fontawesome-free CSS</td>
					<td>jsdelivr</td>
					<td><strong>4.40 s</strong></td>
					<td>100 KB</td>
			</tr>
			<tr>
					<td>lunr.min.js（搜索）</td>
					<td>jsdelivr</td>
					<td><strong>7.45 s</strong></td>
					<td>29 KB</td>
			</tr>
			<tr>
					<td>animate.css / clipboard / lazysizes…</td>
					<td>jsdelivr</td>
					<td>同类量级</td>
					<td>—</td>
			</tr>
			<tr>
					<td>自己的 HTML</td>
					<td>本站</td>
					<td>0.65–2.0 s</td>
					<td>16 KB</td>
			</tr>
			<tr>
					<td><strong>首页总加载</strong></td>
					<td></td>
					<td><strong>7.06 s</strong></td>
					<td></td>
			</tr>
	</tbody>
</table>
<p>也就是说，服务器那 1 到 2 秒是正常的，<strong>剩下的 5 秒全在等别人的 CDN</strong>。</p>
<p>根因不难理解：jsdelivr 早年在中国大陆有加速节点，后来撤了，现在国内访问要绕到境外；而我的 Cloudflare 边缘又恰好落在阿姆斯特丹，一次 TCP + TLS 握手就是 250 毫秒起步，8 个资源叠起来就是好几秒。</p>
<p><strong>这里有个方法论</strong>：慢的时候先分清是「我的问题」还是「别人的问题」。换主机、换框架是最容易想到的动作，但往往不是解法。</p>
<h2 id="二选型三件事不换的两件">二、选型：三件事，不换的两件</h2>
<h3 id="生成器不换">生成器：不换</h3>
<p>Hugo 已经是对的选择：单二进制、零运行时依赖、几百篇文章构建不到一秒、Cloudflare Pages 原生支持。换成 Astro 或 Hexo 只会增加构建复杂度，对这个博客没有任何收益。</p>
<h3 id="主题必须换">主题：必须换</h3>
<p>问题不在 Hugo，在主题。我抓了 11 个主流 Hugo 主题的 demo 站，<strong>只提取真实的资源请求</strong>（<code>&lt;script src&gt;</code>、<code>&lt;link rel=stylesheet&gt;</code>、<code>&lt;img src&gt;</code> 的域名），不猜、不凭印象：</p>
<table>
	<thead>
			<tr>
					<th>主题</th>
					<th>Star</th>
					<th>最近更新</th>
					<th>外部资源引用</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><strong>PaperMod</strong></td>
					<td>14k</td>
					<td>活跃</td>
					<td><strong>0</strong></td>
			</tr>
			<tr>
					<td>hugo-book</td>
					<td>4.1k</td>
					<td>活跃</td>
					<td>0</td>
			</tr>
			<tr>
					<td>hugo-coder</td>
					<td>3.1k</td>
					<td>2026-06</td>
					<td>0（可选功能才拉 CDN）</td>
			</tr>
			<tr>
					<td>Blowfish</td>
					<td>2.9k</td>
					<td>活跃</td>
					<td>Google Tag Manager + cdnjs</td>
			</tr>
			<tr>
					<td>Stack</td>
					<td>6.5k</td>
					<td>2026-05</td>
					<td><strong>Google Fonts</strong></td>
			</tr>
			<tr>
					<td>LoveIt（原来用的）</td>
					<td>3.9k</td>
					<td>活跃</td>
					<td><strong>jsdelivr</strong></td>
			</tr>
			<tr>
					<td>PaperModX</td>
					<td>392</td>
					<td>2025-03 停更</td>
					<td>0</td>
			</tr>
	</tbody>
</table>
<p>PaperMod 是唯一同时满足「开箱零外链 + 持续维护 + 信息密度高」的。而且它有个隐藏优势：<strong>纯 CSS，不含任何 SCSS</strong>，所以不需要 Hugo extended 版本——在 CI 里少一个不确定因素。</p>
<p>它的字体直接用系统字体栈（<code>-apple-system, &quot;Microsoft YaHei&quot;…</code>），图标是内联 SVG，搜索用本地打包的 Fuse.js，全程不碰第三方域名。</p>
<h3 id="部署继续用-cloudflare-pages但换成源码仓库">部署：继续用 Cloudflare Pages，但换成「源码仓库」</h3>
<p>原来的模式是：本地构建 → 把 <code>public/</code> 推上 GitHub → Pages 直接发布产物。这叫<strong>产物仓库</strong>，问题是你没法在网页上改文章、也没法让 CI 帮你构建。</p>
<p>这次改成：<strong>仓库只存源码，Pages 直连私有仓库构建</strong>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">写作 → git push（私有仓库）
</span></span><span class="line"><span class="cl">        ↓ Cloudflare Pages 自动构建（hugo --gc --minify，约 20 秒）
</span></span><span class="line"><span class="cl">        ↓
</span></span><span class="line"><span class="cl">   blog.xbta.cc（CF 边缘）
</span></span></code></pre></div><p>顺带说个容易被忽略的事实：Pages 的<strong>静态请求和带宽是无限免费的</strong>，只有 Pages Functions 才计入 Workers 配额。免费版的限制只有 500 次构建/月、2 万个文件、单文件 25 MB——对个人博客完全够用。</p>
<h2 id="三改造不碰主题源码">三、改造：不碰主题源码</h2>
<p>我一上来给自己定了条硬规矩：<strong><code>themes/PaperMod/</code> 一个字节都不许改</strong>。</p>
<p>理由是可持续性。改主题源码的后果是：主题一升级就冲突，改了三处忘了一处，半年后自己都看不懂改了什么。正确姿势是把所有定制落在两个地方：</p>
<table>
	<thead>
			<tr>
					<th>位置</th>
					<th>干什么</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>hugo.yaml</code></td>
					<td>站点与主题参数（默认浅色、目录、菜单、分页、搜索配置）</td>
			</tr>
			<tr>
					<td><code>assets/css/extended/custom.css</code></td>
					<td>字号、行宽、表格、代码块、列表密度</td>
			</tr>
	</tbody>
</table>
<p><code>assets/css/extended/</code> 是 PaperMod 官方指定的定制入口，主题会主动加载它。这样以后升级主题，直接整个目录覆盖，零冲突。</p>
<p>具体定制按我自己的阅读习惯来：正文 760px、17px 字号、1.75 行高、浅色主题默认、列表不要卡片（一条细线分隔就够）、表格横向滚动不撑破版心。</p>
<h2 id="四图片14-mb--15-mb">四、图片：14 MB → 1.5 MB</h2>
<p>36 张历史配图原来挂在 jsdelivr 的 GitHub 直链上，同样慢。这次全部迁到自己的 R2 图床：</p>
<ul>
<li><strong>格式</strong>：Pillow 转 WebP，质量 82，长边限制 1600px</li>
<li><strong>结果</strong>：14 MB → <strong>1.48 MB，省了 89%</strong>（单张典型值 225 KB → 28 KB）</li>
<li><strong>缓存</strong>：对象上直接写 <code>Cache-Control: public, max-age=31536000, immutable</code></li>
<li><strong>访问</strong>：<code>img.xbta.cc/blog/2020/xxx.webp</code></li>
</ul>
<p>这里有个反直觉的发现：<strong>R2 的自定义域名不走 Cloudflare 的缓存层</strong>。我一开始给它配了一条「边缘缓存一年」的 Cache Rule，测出来 <code>cf-cache-status</code> 永远是 <code>DYNAMIC</code>，规则完全不生效。真正起作用的是对象自带的 <code>Cache-Control</code>——浏览器第二次访问直接命中本地缓存，零请求。</p>
<p>也就是说：R2 靠的是「浏览器缓存」而不是「边缘缓存」。知道这一点，就不用再浪费时间调规则了。</p>
<h2 id="五结果">五、结果</h2>
<table>
	<thead>
			<tr>
					<th>指标</th>
					<th>改造前</th>
					<th>改造后</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>首页完全加载</td>
					<td>7.06 s</td>
					<td><strong>约 1.1 s</strong></td>
			</tr>
			<tr>
					<td>外部资源请求</td>
					<td>8 个</td>
					<td><strong>0</strong></td>
			</tr>
			<tr>
					<td>首页 HTML</td>
					<td>16 KB + 8 个跨域</td>
					<td>8.8 KB（gzip 3.4 KB）</td>
			</tr>
			<tr>
					<td>CSS</td>
					<td>71 KB + 外链 100 KB</td>
					<td>19.7 KB（gzip 5.1 KB）</td>
			</tr>
			<tr>
					<td>图片总量</td>
					<td>14 MB（境外直链）</td>
					<td>1.48 MB（自有图床）</td>
			</tr>
			<tr>
					<td>LCP / CLS</td>
					<td>—</td>
					<td>1.1–1.4 s / 0.000</td>
			</tr>
	</tbody>
</table>
<p>现在整个首屏就是<strong>一个 HTML + 一个 CSS，加起来 28 KB</strong>，剩下的全是可缓存的图片。</p>
<h2 id="六踩过的四个坑">六、踩过的四个坑</h2>
<p><strong>1. 图片下载的 403 不是限流，是 UA</strong></p>
<p>批量迁移图片时，脚本一直返回 403。我先怀疑是代理限流，换了两轮实现才发现：<code>python urllib</code> 的默认 User-Agent 被源站拒了，加上浏览器 UA 立刻正常。</p>
<p>教训：<strong>批量跑之前先用一张图把链路打通</strong>（UA、长度校验、能不能被解析），别写完脚本再来试错。</p>
<p><strong>2. 旧主题的短代码，新主题没有</strong></p>
<p>文章里用了 LoveIt 的 <code>{{&lt; admonition &gt;}}</code> 提示块，换主题后构建直接失败：<code>template for shortcode &quot;admonition&quot; not found</code>。迁内容前应该先 grep 一遍所有短代码。</p>
<p><strong>3. 改卡片样式漏了一个 border</strong></p>
<p>PaperMod 的文章列表项默认是卡片（背景 + 圆角 + 边框 + 悬浮上移）。我去掉了背景和圆角，截图一看还是卡片——因为 <code>border: 1px solid</code> 还在。<strong>覆盖第三方样式必须对照它的源 CSS 逐条覆盖</strong>，只改显眼的那几个属性一定留尾巴。</p>
<p><strong>4. 窄屏代码块溢出，首屏截图看不出来</strong></p>
<p>手机宽度下代码块被右侧裁切，但首页截图完全正常——问题在页面下半部分。后来改成整页长截图 + 直接读 <code>scrollWidth</code> 和 <code>clientWidth</code> 才定位到。视觉验收不能只看首屏。</p>
<h2 id="七一点想法天花板在哪">七、一点想法：天花板在哪</h2>
<p>这套方案把「我能控制的部分」做到底了，但还有一段绕不过去：<strong>Cloudflare 免费版从中国大陆访问，TTFB 在 0.6–2 秒之间波动</strong>，因为边缘节点稳定落在我控制不了的地方（实测是阿姆斯特丹）。</p>
<p>这不是代码能优化掉的，是地理位置。想根治只有一条路：<strong>域名备案 + 境内 CDN</strong>，那是另一个成本和流程问题。</p>
<p>所以我的策略是承认它、然后绕过它：<strong>既然单次往返省不掉，那就让往返次数降到最少</strong>。1 个 HTML + 1 个 CSS，第一个字节到了页面就基本成型，剩下的图片慢慢来也不影响阅读。</p>
<p>体感上的「快」，很多时候不是 RTT 变小了，而是<strong>等待的东西变少了</strong>。</p>
<h2 id="最后">最后</h2>
<p>这次重建真正的收获不是换了什么主题，而是那句最朴素的话：<strong>先量，再改</strong>。</p>
<p>如果不做那次体检，我大概会以为是自己服务器不行，去折腾主机配置、上什么加速服务——而那些全都解决不了问题，因为问题从头到尾都在别人的 CDN 上。</p>
]]></content:encoded></item></channel></rss>