发布于: -/最后更新: -/3 分钟/

Cloudflare Workers 全栈应用:正确使用 Cache API 降低请求消耗

摘要

在使用 Cloudflare Workers 构建全栈应用时,仅设置 HTTP 缓存头(如 Cache-Control)并不足以触发 CDN 缓存。要利用 Cloudflare 的边缘节点缓存,必须显式使用 Cache API。正确的方法是:在 Worker 内部手动拦截请求,先去缓存中查询,如果未命中,则执行回源逻辑,并将结果写入缓存。可以使用 TypeScript 实现模板,包括构建 Cache Key、检查缓存命中、回源获取数据、构造新的响应用于缓存、设置缓存头和写入缓存。另外,可以配合 Purge API 实现“激进缓存”,通过编程式刷新保证内容的实时性。

在使用 Cloudflare Workers 构建全栈应用时,很多开发者会遇到一个误区:以为直接在 Worker 的 Response 中设置 HTTP 缓存头(如 Cache-Control),就能自动触发 CDN 缓存。

事实并非如此。

如果只设置响应头,请求依然会穿透 CDN 到达你的 Worker,导致 cf-cache-status 无法出现。这意味着每次请求都会消耗 Worker 的额度(Requests)和计算时间(CPU Time)。

要想真正利用 Cloudflare 的边缘节点缓存,从而节省 Worker 调用次数,必须显式使用 Cache API

1. 正确姿势:使用 Cache API

我们需要在 Worker 内部手动拦截请求,先去 caches.default 查询。如果未命中,再执行回源逻辑,并将结果写入缓存。

以下是一个标准的 TypeScript 实现模板:

TypeScript
interface Env {}

export default {
  async fetch(request, env, ctx): Promise<Response> {
    const cacheUrl = new URL(request.url);

    // 1. 构建 Cache Key
    // 注意:通常使用 request 对象或 url 字符串作为 key
    const cacheKey = new Request(cacheUrl.toString(), request);
    const cache = caches.default;

    // 2. 检查是否命中缓存
    let response = await cache.match(cacheKey);

    if (!response) {
      console.log(
        `[Cache Miss] ${request.url}. Fetching and caching request.`
      );
      
      // 3. 回源获取数据 (这里可以是 upstream fetch,也可以是数据库查询结果的组装)
      response = await fetch(request);

      // 4. 构造新的响应用于缓存
      // 必须重新构造 Response,因为原本的 response body 流只能读取一次
      response = new Response(response.body, response);
      
      // 5. 设置缓存头
      // s-maxage 控制 CDN 缓存时间
      response.headers.append("Cache-Control", "s-maxage=10");
      
      // 6. 写入缓存
      // 使用 ctx.waitUntil 确保返回响应给用户后,写入缓存的操作不会被杀掉
      ctx.waitUntil(cache.put(cacheKey, response.clone()));
    } else {
      console.log(`[Cache Hit] ${request.url}.`);
    }
    
    return response;
  },
} satisfies ExportedHandler<Env>;

2. 进阶策略:配合 Purge API 实现“激进缓存”

一旦掌握了 Cache API,你就可以采用更激进的缓存策略(例如设置很长的 s-maxage),然后通过编程式刷新(Purge)来保证内容的实时性。

Cloudflare 提供了强大的 Purge API,支持按精确 URL或**前缀(Prefix)**清除缓存。你需要先在 Dashboard 获取一个拥有 Zone.Cache:Purge 权限的 Token。

TypeScript
const purgeCache = async () => {
  const response = await fetch(
    `https://api.cloudflare.com/client/v4/zones/${CLOUDFLARE_ZONE_ID}/purge_cache`,
    {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${CLOUDFLARE_PURGE_API_TOKEN}`,
        "Content-Type": "application/json",
      },
      // 注意:body 需要序列化为字符串
      body: JSON.stringify({
        files: ["https://example.com/about"], // 精确清除
        prefixes: ["https://example.com/posts"], // 按前缀清除(清除该路径下所有资源)
      }),
    },
  );

  return response.json();
};

这样就可以做到及时更新页面

正文结束