<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh_CN">
  <title>Birdjiujiuuu の 小窝</title>
  <subtitle>喜欢您来喵~</subtitle>
  <link href="https://birdjiujiuuu.pages.dev/" rel="alternate" type="text/html"/>
  <link href="https://birdjiujiuuu.pages.dev/atom.xml" rel="self" type="application/atom+xml"/>
  <id>https://birdjiujiuuu.pages.dev/</id>
  <updated>2026-10-31T00:00:00.000Z</updated>
  <entry>
    <title>使用边缘节点加速你的网站优化国内用户体验</title>
    <link href="https://birdjiujiuuu.pages.dev/posts/20261031/" rel="alternate" type="text/html"/>
    <id>https://birdjiujiuuu.pages.dev/posts/20261031/</id>
    <published>2026-10-31T00:00:00.000Z</published>
    <updated>2026-10-31T00:00:00.000Z</updated>
    <summary>使用阿里云 ESA 免费版加速 GitHub Pages 主站与 Cloudflare Pages 图床，并解决配置过程中遇到的问题。</summary>
    <content type="html"><![CDATA[<p>在搭建个人博客和图床时，为了兼顾免费与稳定，我们通常会选择 GitHub Pages 或 Cloudflare Pages 作为源站托管服务。然而，由于跨境网络路由的波动，国内用户访问这些海外节点的速度往往很一般。</p>
<p>为了解决这个问题，我们可以使用 <strong>边缘安全加速</strong> ，以此来提高国内用户的访问速度。</p>
<h2>架构说明</h2>
<p>为了便于说明，本文使用以下域名代称，加速服务使用 阿里云 ESA 免费版：</p>
<table>
<thead>
<tr>
<th>角色</th>
<th>域名 / 地址</th>
</tr>
</thead>
<tbody>
<tr>
<td>根域名</td>
<td><code>example.com</code></td>
</tr>
<tr>
<td>主站地址</td>
<td><code>www.example.com</code>（源站：GitHub Pages <code>username.github.io</code>）</td>
</tr>
<tr>
<td>图床地址</td>
<td><code>imgs.example.com</code>（源站：Cloudflare Pages <code>project.pages.dev</code>）</td>
</tr>
</tbody>
</table>
<h2>前置准备</h2>
<p>无论是加速博客主站，还是加速图床，下面几步都是共通的，这是接入 ESA 的基础。</p>
<h3>添加站点与验证</h3>
<ol>
<li>在阿里云 ESA 控制台添加站点 <code>example.com</code>，选择 <strong>CNAME 接入</strong>。</li>
</ol>
<p>::: important 关于获得加速与保护的区域</p>
<ul>
<li>全球（不包含中国内地）：适合未备案的域名，但是实际加速效果有限。</li>
<li>全球：适合备案的域名，加速效果显著。
:::</li>
</ul>
<ol>
<li>添加完成后，系统要求验证域名所有权。在域名托管平台的 <code>Advanced DNS</code> 添加一条 <code>TXT Record</code>，主机记录为 <code>_esaauth</code>，值填 ESA 提供的字符串。</li>
</ol>
<p>::: note 注意
<code>_esaauth</code> 验证成功后<strong>请务必保留</strong>，这是后续证书自动续期的凭证。
:::</p>
<h3>添加 DNS 记录（ESA 侧）</h3>
<p>在阿里云 ESA 控制台的站点管理中，进入 <strong>DNS</strong> → <strong>记录</strong> 页面：</p>
<ol>
<li>添加一条指向博客源站的记录：主机记录填 <code>www</code>，记录类型选 <code>CNAME</code>，记录值填 <code>username.github.io</code>，并<strong>开启代理加速</strong>。</li>
<li>如果需要图床，再添加一条：主机记录填 <code>imgs</code>，记录类型选 <code>CNAME</code>，记录值填 <code>project.pages.dev</code>，同样<strong>开启代理加速</strong>。</li>
</ol>
<h3>修改 DNS 解析（域名托管平台侧）</h3>
<p>在域名托管平台的后台，清理掉原来指向 GitHub IP 的所有 <code>@</code> 记录，以及指向 <code>github.io</code> 和 <code>pages.dev</code> 的旧 CNAME 记录，然后全部替换为 ESA 分配给你的专属 CNAME 地址：</p>
<table>
<thead>
<tr>
<th>Host</th>
<th>Value</th>
<th>TTL</th>
</tr>
</thead>
<tbody>
<tr>
<td>根域名 <code>@</code> 或需要加速的子域名（如 <code>www</code> 或 <code>imgs</code>）</td>
<td>阿里云给你提供的对应域名加速地址</td>
<td>自动 (Automatic)</td>
</tr>
</tbody>
</table>
<h3>申请 SSL 证书</h3>
<ol>
<li>在 ESA 控制台选择 <strong>站点管理</strong> ，左侧导航栏选择 <strong>SSL/TLS</strong> → <strong>边缘证书</strong>，页面下方可以看到 <strong>托管 DCV</strong> 区域。</li>
<li>在 <strong>托管 DCV</strong> 区域，复制系统为你生成的 CNAME 信息（免费证书对应的是 <code>_acme-challenge</code> 开头的记录），并在域名托管平台添加对应的 CNAME 记录：</li>
</ol>
<table>
<thead>
<tr>
<th>Host</th>
<th>Value</th>
<th>TTL</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>_acme-challenge</code>（非泛域名则填 <code>_acme-challenge.&lt;子域名&gt;</code>，如 <code>_acme-challenge.www</code>）</td>
<td>控制台复制的完整 CNAME 目标地址（<code>&lt;hostname&gt;</code>为你的域名）</td>
<td>自动 (Automatic)</td>
</tr>
</tbody>
</table>
<p>::: note 注意
该 CNAME 记录<strong>请务必保留</strong>，这是后续证书自动续期的凭证。
:::</p>
<ol>
<li>完成 DCV 配置并等待 DNS 生效后，在 <strong>证书管理</strong> 板块，点击 <strong>申请免费证书</strong>：</li>
</ol>
<ul>
<li><strong>证书颁发机构</strong>：选择 <code>Let's Encrypt</code></li>
<li><strong>证书域名</strong>：填入你需要用到的<strong>单域名</strong>，例如：<code>example.com, www.example.com, imgs.example.com</code></li>
</ul>
<blockquote>
<p><em>提交后，由于 DCV 记录已经提前配好，系统会自动完成验证，证书通常几分钟内即可签发。</em></p>
</blockquote>
<ol>
<li>证书签发完成后，开启下方的 <strong>强制 HTTPS</strong>。</li>
</ol>
<h2>主站加速配置（GitHub Pages）</h2>
<p>证书签发后，访问主站大概率会遇到 <code>ERR_TOO_MANY_REDIRECTS</code>（重定向次数过多）。</p>
<p>::: tip 为什么会死循环？
GitHub Pages 默认开启了强制 HTTPS。如果 ESA 用 HTTP 协议回源，GitHub 会返回 301 跳转回 HTTPS；ESA 如果没处理好这个跳转，浏览器就会在 HTTP 和 HTTPS 之间无限循环。
:::</p>
<h3>配置主站回源规则</h3>
<p>进入 ESA 控制台 → <strong>规则</strong> → <strong>回源规则</strong>，为主站新建一条规则：</p>
<table>
<thead>
<tr>
<th>参数</th>
<th>填写内容</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>匹配条件</strong></td>
<td>主机名等于 <code>www.example.com</code>（或 <code>example.com</code>）</td>
</tr>
<tr>
<td><strong>回源协议</strong></td>
<td><strong>HTTPS</strong>，端口 <strong>443</strong></td>
</tr>
<tr>
<td><strong>回源 Host</strong></td>
<td><strong><code>www.example.com</code></strong></td>
</tr>
<tr>
<td><strong>回源 SNI</strong></td>
<td><strong><code>www.example.com</code></strong></td>
</tr>
</tbody>
</table>
<p>配置保存后，重新访问主站，重定向问题就会消失。</p>
<h2>图床加速配置（Cloudflare Pages）</h2>
<p>此时，访问 <code>imgs.example.com</code> 可能会无法访问或直接返回 <code>403 Forbidden</code>，然而直接访问 <code>project.pages.dev</code> 一切正常。</p>
<p>::: tip 为什么会 403？
Cloudflare Pages 对回源请求的校验极其严格。Cloudflare 依赖请求头里的 <code>Host</code> 和 TLS 握手里的 <code>SNI</code>（服务器名称指示）来识别你要访问哪个站。如果 ESA 发过去的身份对不上，就会被直接拒绝服务。
:::</p>
<h3>配置图床回源规则</h3>
<p>为了避免与主站冲突，需要在 ESA 中<strong>单独为图床新建一条回源规则</strong>，并确保其<strong>优先级高于主站规则</strong>：</p>
<table>
<thead>
<tr>
<th>参数</th>
<th>填写内容</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>匹配条件</strong></td>
<td>主机名等于 <code>imgs.example.com</code></td>
</tr>
<tr>
<td><strong>回源协议</strong></td>
<td><strong>HTTPS</strong>，端口 <strong>443</strong></td>
</tr>
<tr>
<td><strong>回源 Host</strong></td>
<td><strong><code>imgs.example.com</code></strong></td>
</tr>
<tr>
<td><strong>回源 SNI</strong></td>
<td><strong><code>imgs.example.com</code></strong></td>
</tr>
</tbody>
</table>
<p>添加完回源规则后，你的自定义域名应该能够正常访问图床了。</p>
]]></content>
    <author><name>Birdjiujiuuu</name></author>
    <category term="技术"/>
  </entry>
  <entry>
    <title>使用 Cloudflare 创建属于自己的免费图床</title>
    <link href="https://birdjiujiuuu.pages.dev/posts/20261028/" rel="alternate" type="text/html"/>
    <id>https://birdjiujiuuu.pages.dev/posts/20261028/</id>
    <published>2026-10-28T00:00:00.000Z</published>
    <updated>2026-10-28T00:00:00.000Z</updated>
    <summary>如何使用 Cloudflare 创建属于自己的免费图床。</summary>
    <content type="html"><![CDATA[<p>在写博客或者做个人项目时，一个稳定且免费的图床必不可少。目前市面上的图床要么限制多，要么面临随时跑路的风险。今天记录一下如何使用 <strong>Cloudflare R2 + Pages</strong> 搭建一个属于自己的免费图床。</p>
<p>这个方案有几个巨大的优势：</p>
<ol>
<li><strong>免流量的费用</strong>：Cloudflare R2 的读取操作和流量完全免费。</li>
<li><strong>国内访问友好</strong>：使用 Pages 提供的 <code>pages.dev</code> 域名，相比被墙的 <code>workers.dev</code> 来说，在国内很多时候可以直连，速度尚可。</li>
<li><strong>防超额扣费</strong>：通过 Pages Functions 代理和强缓存，配合 Fail closed 机制，杜绝了超额扣费的风险。</li>
<li><strong>目录结构自定义</strong>：直接映射 R2 中的文件夹结构，无需重新部署即可上传新图。</li>
</ol>
<hr />
<h2>准备工作</h2>
<ul>
<li>一个 Cloudflare 账号。</li>
<li>一张银行卡（储蓄卡或信用卡都可以）或 PayPal（开通 R2 需要验证，但不会产生费用）。</li>
</ul>
<h2>第一步：创建并配置 R2 存储桶</h2>
<ol>
<li>登录 Cloudflare 控制台，在左侧导航栏找到并进入 <strong>R2</strong>。</li>
<li>点击 <strong>创建存储桶</strong>，并给你的图床起个名字，例如 <code>images</code>，位置选择 <strong>自动（亚太地区）</strong>，默认存储类选择 <strong>标准</strong>。</li>
<li>创建完成后，进入该存储桶的 <strong>设置</strong> 页面。</li>
<li><strong>默认情况下，存储桶不能公开访问，但还是建议确认一下。</strong> 找到 <strong>公共开发 URL</strong>，确认其为关闭的。我们要保证存储桶是私有的，所有访问必须走我们自己的 Pages。</li>
<li>在存储桶中点击 <strong>创建文件夹</strong> ，然后进去上传图片。比如创建一个 <code>example</code> 文件夹，里面再建一个 <code>album</code> 文件夹，上传一张 <code>01.jpg</code>，它的 Key 就是 <code>example/album/01.jpg</code>。</li>
</ol>
<h2>第二步：创建 Pages 项目并绑定 R2</h2>
<ol>
<li>在控制台进入 <strong>Workers 和 Pages</strong>，创建一个新的 Pages 项目（Pages 项目在底下的小字部分），选择 <strong>拖放文件</strong> 选项。</li>
<li>进入 Pages 项目的 <strong>设置</strong> -&gt; <strong>绑定</strong>。</li>
<li>点击 <strong>添加</strong> -&gt; <strong>R2 存储桶</strong>：
<ul>
<li><strong>变量名称</strong> 填入 <code>Images</code>（代码里会用到，请保持一致，注意大小写）。</li>
<li><strong>R2 存储桶</strong> 选择你刚创建的 <code>images</code>。</li>
</ul>
</li>
<li>保存后，<strong>必须重新部署一次项目</strong>，绑定才会生效。</li>
</ol>
<h2>第三步：编写 Functions 代码</h2>
<p>我们要让 Pages 在根目录下拦截所有请求，去 R2 里找对应的图片。</p>
<p>由于在 Cloudflare Pages 中，要使 functions 生效，必须要使用 Wrangler 部署。因此，我们在电脑的空白文件夹下创建 <code>functions/[[path]].js</code> 文件，并写入以下代码：</p>
<pre><code>export async function onRequest(context) {
  const { request, env, params } = context;

  if (request.method !== "GET" &amp;&amp; request.method !== "HEAD") {
    return new Response("Method Not Allowed", { status: 405 });
  }

  // params.path 是路径片段数组，例如 /example/01.jpg → ["example", "01.jpg"]
  const rawKey = (params.path || []).join("/");

  // 安全清理，防止 ../ 路径遍历
  const key = rawKey
    .split("/")
    .filter((segment) =&gt; segment &amp;&amp; segment !== "." &amp;&amp; segment !== "..")
    .join("/");

  // 根路径 / 没有对应对象，返回一个简单提示
  if (!key) {
    return new Response("Image Hosting", { status: 200 });
  }

  // 这里的 Images 应该和上面 R2 存储桶的变量名称一致
  const object = await env.Images.get(key);
  if (object === null) {
    return new Response("Not Found", { status: 404 });
  }

  const headers = new Headers();
  object.writeHttpMetadata(headers);
  headers.set("etag", object.httpEtag);
  headers.set("Cache-Control", "public, max-age=31536000, immutable");

  if (request.method === "HEAD") {
    return new Response(null, { headers });
  }

  return new Response(object.body, { headers });
}
</code></pre>
<h2>第四步：使用 Wrangler 部署项目</h2>
<ol>
<li>确保你的电脑安装了 <code>Node.js</code>。</li>
<li>打开终端（命令行），进入你刚刚创建 <code>functions</code> 文件夹的根目录。</li>
<li>运行以下命令进行部署（第一次运行可能会提示你登录 Cloudflare 账号，按提示授权即可）：</li>
</ol>
<pre><code>npx wrangler pages deploy . --project-name=&lt;你的项目名称&gt;
</code></pre>
<p>部署成功后，终端会返回一个预览或生产环境的 URL。</p>
<h2>第五步：设置防超额扣费</h2>
<p>这是整个方案中最核心的一步。由于 R2 免费额度用完后是 <strong>自动按量计费</strong> 的，为了确保万无一失，我们需要利用 Pages Functions 的额度限制来间接锁死 R2 的消耗。</p>
<ol>
<li>
<p>进入你的 Pages 项目，点击 <strong>设置</strong> -&gt; <strong>运行时</strong>。</p>
</li>
<li>
<p>找到 <strong>失败开放 (Fail open)</strong> / <strong>失败关闭 (Fail closed)</strong> 选项，将其修改为 <strong>失败关闭 (Fail closed)</strong>，然后保存。</p>
</li>
</ol>
<p>::: tip 为什么选 Fail closed？
Pages Functions 的免费额度是 <strong>每天 10 万次请求</strong>，当 10 万次额度用尽后，Pages 会直接停止工作（返回 1027 错误），而不会继续绕过 Function 去请求后面的资源。这样一来，你的 R2 读取次数被死死卡在每天 10 万次以内，远远低于 R2 免费提供的每月 1000 万次读取额度，这样就可以有效防止超额扣费。
:::</p>
<h2>第六步：测试与验证</h2>
<p>部署完成后，在浏览器中打开你的图片链接：</p>
<pre><code>https://你的项目名称.pages.dev/example/album/01.jpg
</code></pre>
<p>如果图片正常显示，说明基本配置已经成功。至此，你的专属免费图床已经搭建完毕。</p>
<h2>（可选）设置自定义域名</h2>
<p>如果你希望图床链接使用自己的域名，可以在这一步继续配置，整个配置过程完全 <strong>不影响你已有的主域名或其他子域名解析</strong>。</p>
<ol>
<li><strong>在 Cloudflare Pages 中添加域名</strong></li>
</ol>
<ul>
<li>
<p>在 Cloudflare 中选择你的图床项目，点击 <strong>自定义域</strong> -&gt; <strong>设置自定义域</strong>，输入你想要的子域名（如 <code>imgs.你的域名.com</code>）并继续。</p>
</li>
<li>
<p>页面会给出一个 <strong>CNAME 目标地址</strong>（通常是你的 <code>项目名.pages.dev</code>），请记录下来。</p>
</li>
</ul>
<ol>
<li><strong>在域名服务商处配置解析</strong></li>
</ol>
<ul>
<li>
<p>登录你的域名后台，进入 <strong>Advanced DNS</strong> 选项。</p>
</li>
<li>
<p>添加一条新的 <strong>CNAME Record</strong> 并保存：</p>
</li>
</ul>
<table>
<thead>
<tr>
<th>Host</th>
<th>Value</th>
<th>TTL</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>imgs</code>（你的子域名前缀）</td>
<td>刚才记录下的 <strong>CNAME 目标地址</strong></td>
<td>自动 (Automatic)</td>
</tr>
</tbody>
</table>
<p>:::note 注意
由于只修改子域名，你无需将整个域名的 NS 迁移到 Cloudflare。
:::</p>
<ol>
<li><strong>等待生效</strong>
DNS 通常需要几分钟到半小时生效。回到 Cloudflare Pages 的页面，当状态变为 <strong>“活动” (Active)</strong> 时，说明配置成功，SSL 证书也会自动签发。</li>
</ol>
<p>配置完成后，你原有的 <code>pages.dev</code> 地址依然可以访问。但在博客中引用图片时，你可以使用你自己的专属链接了，例如将 <code>https://你的项目名称.pages.dev/example/album/01.jpg</code> 替换为 <code>https://imgs.你的域名.com/example/album/01.jpg</code>。</p>
<h2>日常使用指南</h2>
<ul>
<li>
<p><strong>上传新图</strong>：直接在 Cloudflare R2 控制台进入对应的文件夹上传即可，<strong>无需再次运行 Wrangler 部署</strong>，URL 会自动生效。</p>
</li>
<li>
<p><strong>引用图片</strong>：在博客或论坛中，直接使用 <code>https://你的项目名称.pages.dev/文件夹/子文件夹/文件名.jpg</code> 即可。</p>
</li>
<li>
<p><strong>代码备份</strong>：请务必妥善保存本地的 <code>functions/[[path]].js</code> 文件。由于我们使用的是直接上传部署，云端的代码只是编译后的产物，本地代码可以帮助你日后修改配置。</p>
</li>
</ul>
]]></content>
    <author><name>Birdjiujiuuu</name></author>
    <category term="技术"/>
  </entry>
</feed>
