Advanced (更新: 2026/6/2)

Claude Code 边缘计算实战:Workers、缓存与地域分流

用Claude Code设计Edge runtime:Workers、地域分流、缓存键、常见坑和验证命令。

Claude Code 边缘计算实战:Workers、缓存与地域分流

边缘计算不是把所有后端都搬到离用户最近的地方,而是把“小而快的判断”放到靠近用户的运行环境里。比如地域跳转、短时间缓存、A/B测试分桶、预览页面的轻量鉴权,都可以在请求进入主应用之前完成。

但Edge runtime并不是万能加速按钮。它和普通Node.js服务器不同:可用API、执行时间、依赖包、请求元数据和缓存行为都有边界。如果只对Claude Code说“帮我改成边缘部署”,它很可能混入Node专用API,忘记把语言写进缓存键,或者把国家代码当成计费和权限判断依据。

本文以2026年6月2日确认过的官方资料为基础,整理Claude Code提示词、Cloudflare Workers可复制代码、Vercel Edge Middleware示例、迁移到Deno Deploy时的注意点,以及上线前验证命令。建议同时打开Cloudflare WorkersWorkers Request APIWorkers Cache APIVercel Edge RuntimeDeno Deploy runtimeClaude Code overview。如果要深入单个平台,可以继续看Claude Code Cloudflare Workers指南Claude Code Vercel Edge Functions指南

先决定哪些逻辑适合Edge

最重要的第一步,是区分“入口判断”和“核心业务”。如果逻辑只需要URL、请求头、Cookie或很小的请求体,通常适合放在Edge。相反,深度数据库查询、长时间LLM调用、图片处理、PDF生成、账单结算和后台任务,最好留在普通API或队列中。

使用场景适合Edge的原因应留在后端的内容
国家或语言分流可用国家代码或语言头快速选择入口税费、发票、库存确认
短时间缓存公开响应可以在近端复用DB更新和重新生成任务
A/B测试分桶页面渲染前即可读取Cookie统计判断和收入分析
轻量鉴权门禁预览页、管理页可提前拦截会话签发和权限审计
Webhook入口校验小请求体的签名可快速验证支付处理、邮件、重试

把这张表放进Claude Code提示词,可以显著减少误生成。它会更清楚哪些代码应该在Edge里完成,哪些应该转交给源站API。

flowchart LR
  User["User request"] --> Edge["Edge runtime"]
  Edge --> Geo["Country / language branch"]
  Edge --> Cache["Short cache"]
  Edge --> Gate["Light auth gate"]
  Edge --> Origin["Origin API / database"]
  Origin --> Queue["Queue or background work"]

给Claude Code的提示词

边缘实现的提示词不要只写“让它更快”,而要写清楚限制。Claude Code能很快产出代码,但缓存键、地域分支、secret处理、验证命令这些细节如果不写明,最容易遗漏。

Implement a small Cloudflare Workers + TypeScript Edge runtime API.

Files:
- wrangler.toml
- src/index.ts

Requirements:
- GET /health returns JSON
- GET /api/edge-content returns locale and CTA copy based on country
- Use request.cf.country when Cloudflare provides it
- Also support a CF-IPCountry header for local curl verification
- Cache each locale separately for 60 seconds with the Cache API
- Return cache hit/miss through X-Edge-Cache
- Use Web APIs such as Request, Response, URL, and crypto; do not use Node-only APIs
- Include at least three curl or build commands that verify the behavior

Do not:
- Use pseudocode
- Hard-code API tokens or secrets
- Treat country code as proof for billing, permissions, or legal decisions

几个术语可以先说明。runtime就是代码运行的环境,cache key是查找缓存响应的名字,colo是Cloudflare数据中心标识。先解释这些词,Claude Code生成的注释和交接说明会更适合初学者。

Cloudflare Workers可复制示例

下面的Worker保持很小。/api/edge-content会读取Cloudflare请求元数据或本地测试请求头,将国家映射到语言,并用Cache API缓存60秒。本地可以用CF-IPCountry请求头模拟国家,所以不需要部署后才发现分流写错。

name = "claude-edge-router"
main = "src/index.ts"
compatibility_date = "2026-06-02"

[vars]
DEFAULT_LOCALE = "en"
// src/index.ts
export interface Env {
  DEFAULT_LOCALE: string;
}

type Locale = "ja" | "en" | "zh" | "ko" | "es" | "fr" | "de" | "pt" | "hi" | "id";

type CfRequest = Request & {
  cf?: {
    country?: string;
    colo?: string;
    city?: string;
  };
};

const SUPPORTED_LOCALES = new Set<Locale>([
  "ja",
  "en",
  "zh",
  "ko",
  "es",
  "fr",
  "de",
  "pt",
  "hi",
  "id",
]);

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

    if (url.pathname === "/health") {
      return json({ ok: true, runtime: "cloudflare-workers" });
    }

    if (url.pathname === "/api/edge-content") {
      return handleEdgeContent(request, env, ctx);
    }

    return json({ error: "not_found" }, 404);
  },
} satisfies ExportedHandler<Env>;

async function handleEdgeContent(
  request: Request,
  env: Env,
  ctx: ExecutionContext
): Promise<Response> {
  const url = new URL(request.url);
  const country = getCountry(request);
  const locale = localeFromCountry(country, env.DEFAULT_LOCALE);
  const cacheKey = buildCacheKey(request, locale);
  const cached = await caches.default.match(cacheKey);

  if (cached) {
    return withHeaders(cached, {
      "X-Edge-Cache": "HIT",
      "X-Edge-Locale": locale,
    });
  }

  const body = {
    locale,
    country,
    colo: getColo(request),
    path: url.pathname,
    cta: localizedCta(locale),
    generatedAt: new Date().toISOString(),
  };

  const response = json(body, 200, {
    "Cache-Control": "public, max-age=0, s-maxage=60, stale-while-revalidate=300",
    "X-Edge-Cache": "MISS",
    "X-Edge-Locale": locale,
  });

  ctx.waitUntil(caches.default.put(cacheKey, response.clone()));
  return response;
}

function buildCacheKey(request: Request, locale: Locale): Request {
  const url = new URL(request.url);
  url.search = "";
  url.pathname = `/__edge-cache/${locale}${url.pathname}`;
  return new Request(url.toString(), { method: "GET" });
}

function getCountry(request: Request): string {
  const cf = (request as CfRequest).cf;
  return (
    request.headers.get("CF-IPCountry") ||
    request.headers.get("x-country") ||
    cf?.country ||
    "US"
  ).toUpperCase();
}

function getColo(request: Request): string {
  return (request as CfRequest).cf?.colo || "local";
}

function localeFromCountry(country: string, fallback: string): Locale {
  const normalizedFallback = SUPPORTED_LOCALES.has(fallback as Locale)
    ? (fallback as Locale)
    : "en";

  switch (country) {
    case "JP":
      return "ja";
    case "CN":
    case "TW":
    case "HK":
      return "zh";
    case "KR":
      return "ko";
    case "ES":
    case "MX":
    case "AR":
      return "es";
    case "FR":
      return "fr";
    case "DE":
      return "de";
    case "BR":
    case "PT":
      return "pt";
    case "IN":
      return "hi";
    case "ID":
      return "id";
    default:
      return normalizedFallback;
  }
}

function localizedCta(locale: Locale): string {
  const messages: Record<Locale, string> = {
    ja: "Claude Code教材を見る",
    en: "Explore Claude Code templates",
    zh: "查看 Claude Code 教材",
    ko: "Claude Code 템플릿 보기",
    es: "Ver plantillas de Claude Code",
    fr: "Voir les modèles Claude Code",
    de: "Claude Code Vorlagen ansehen",
    pt: "Ver modelos de Claude Code",
    hi: "Claude Code टेम्पलेट देखें",
    id: "Lihat template Claude Code",
  };
  return messages[locale];
}

function json(data: unknown, status = 200, headers: HeadersInit = {}): Response {
  return new Response(JSON.stringify(data, null, 2), {
    status,
    headers: {
      "Content-Type": "application/json; charset=utf-8",
      "X-Content-Type-Options": "nosniff",
      ...headers,
    },
  });
}

function withHeaders(response: Response, headers: Record<string, string>): Response {
  const next = new Response(response.body, response);
  for (const [key, value] of Object.entries(headers)) {
    next.headers.set(key, value);
  }
  return next;
}

重点在缓存键。这里把locale写进/__edge-cache/${locale}${pathname},避免中文或日文CTA被错误复用给英语用户。同时清掉查询字符串,避免utm_source之类的广告参数把缓存切成很多无用条目。

扩展到Vercel和Deno

Vercel Edge Middleware适合在页面渲染前设置请求信息,例如国家、A/B测试桶和轻量跳转。使用它时要确认Edge Runtime限制,不要引入依赖Node专用API的库。

// middleware.ts
import { NextRequest, NextResponse } from "next/server";

export const config = {
  matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};

export function middleware(request: NextRequest) {
  const country = request.headers.get("x-vercel-ip-country") || "US";
  const bucket = request.cookies.get("edge_bucket")?.value || chooseBucket();
  const response = NextResponse.next();

  response.headers.set("x-edge-country", country);
  response.headers.set("x-edge-bucket", bucket);
  response.cookies.set("edge_bucket", bucket, {
    maxAge: 60 * 60 * 24 * 30,
    sameSite: "lax",
    secure: true,
  });

  if (country === "JP" && request.nextUrl.pathname === "/pricing") {
    return NextResponse.redirect(new URL("/jp/pricing", request.url));
  }

  return response;
}

function chooseBucket(): "a" | "b" {
  const bytes = new Uint8Array(1);
  crypto.getRandomValues(bytes);
  return bytes[0] < 128 ? "a" : "b";
}

Deno Deploy也可以沿用Web标准的RequestResponse。但不要把Cloudflare专属的request.cf照搬过去;供应商请求头和环境变量不同,应在提示词里明确说明。

// main.ts
Deno.serve((request: Request) => {
  const url = new URL(request.url);
  const country = request.headers.get("x-country") || "US";

  if (url.pathname !== "/api/edge-content") {
    return Response.json({ error: "not_found" }, { status: 404 });
  }

  return Response.json({
    runtime: "deno-deploy",
    country,
    message: country === "JP" ? "Japan entry" : "Default edge entry",
  });
});

验证命令

边缘代码至少要验证类型、本地HTTP、地域分支、缓存头和部署前配置。不要让Claude Code只说“已实现”,要让它留下可复查的命令和结果。

npm create cloudflare@latest claude-edge-router -- --type=hello-world
cd claude-edge-router
npm install -D typescript wrangler
npx wrangler types
npx tsc --noEmit
npx wrangler dev

另开终端执行curl。

curl -i http://127.0.0.1:8787/health
curl -i -H "CF-IPCountry: JP" http://127.0.0.1:8787/api/edge-content
curl -i -H "CF-IPCountry: US" http://127.0.0.1:8787/api/edge-content
curl -i -H "CF-IPCountry: JP" "http://127.0.0.1:8787/api/edge-content?utm_source=test"
npx wrangler deploy --dry-run

预期结果很直接:第一次同路径请求是X-Edge-Cache: MISS,同一语言和路径的下一次请求是HIT。JP映射到ja,US映射到en,带utm_source的请求不会创建单独缓存。

常见坑

第一个坑是把太多工作放到Edge。长LLM调用、复杂ORM查询、PDF生成、图片处理和账单流程通常不适合Edge。Edge应该是入口门禁,不应该承担整个后端。

第二个坑是缓存键设计不当。语言、登录状态、价格展示、实验桶会改变响应时,就必须进入缓存键。反过来,如果把utm_*、时间戳等噪声也放进缓存键,缓存命中率会很低。

第三个坑是过度信任地域信息。request.cf.countryx-vercel-ip-country适合用来设置默认体验,但不能作为税务、年龄、权限、计费的证明。VPN、企业代理和CDN路由都会影响它。

第四个坑是只给Claude Code一个平台名。“用Workers实现”并没有说明要用Cache API、KV、D1、Vercel兼容层还是源站回退。可以结合Claude Code serverless functions性能优化指南来决定边界。

收益导线与下一步

在多语言内容站里,Edge runtime也能改善收益导线。你可以按语言展示免费PDF文案,把读者导向最合适的教材CTA,在缓存前清理广告参数,并让模板购买入口保持快速响应。

需要可复用模板时,可以查看ClaudeCodeLab产品列表。如果团队想统一Claude Code提示词、代码审查标准,以及Edge和Node的边界,可以看Claude Code培训与导入咨询。想先拿到简短流程,可以从免费速查表开始。

实际试用结果

这次更新中,Masa在本地Workers样例里切换CF-IPCountry: JPCF-IPCountry: US,检查了localeX-Edge-Cache以及去掉广告参数后的缓存键。验证时发现,本地测试最好先读取测试请求头,再回退到request.cf.country,否则连续测试时国家分支看起来会被固定住。缓存键也改成/__edge-cache/${locale}${pathname},避免不同地域内容混在一起。给Claude Code下指令时,最好明确要求它同时审查请求头优先级、地域分支和缓存键。

总结

边缘计算不是把一切搬到Edge,而是把能在入口快速判断的逻辑放到近端,重处理和最终判断仍留在普通API。把runtime限制、缓存键、地域分流和验证命令一起交给Claude Code,生成结果会更快,也更容易审查。

#Claude Code #edge computing #Cloudflare Workers #Vercel #performance
免费

免费 PDF: Claude Code 速查表

输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。

我们会妥善保护你的信息,不发送垃圾邮件。

把 Claude Code 变成真正能带来结果的工作流

先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。

Masa

关于作者

Masa

专注 Claude Code 实务流程、团队导入和内容转化的工程师。