Cloudflare Workers如何配置才能避免CORS错误?

你兴冲冲地将图床或API迁移到了Cloudflare R2,并信心满满地部署了一个Cloudflare Worker作为代理网关。然而,当你的前端应用尝试发起请求时,浏览器控制台却无情地抛出了那个熟悉的红色错误:“Access to fetch at ‘…’ from origin ‘…’ has been blocked by CORS policy”。这个场景对于许多初次配置Cloudflare Workers的开发者而言,简直像一盆冷水。CORS(跨源资源共享)错误并非Worker本身的问题,而是一个安全机制,但通过正确的Worker配置,我们可以优雅地解决它,而不是粗暴地禁用浏览器安全策略。

核心策略:在Worker响应中添加CORS头部

问题的根源在于,当你的前端应用(例如运行在 https://your-site.com)向托管在不同源(例如你的Worker域名 https://your-worker.your-account.workers.dev 或绑定的自定义域名)的Worker发起请求时,浏览器会执行“预检”请求。Worker必须返回包含正确CORS头部的响应,明确告诉浏览器:“我允许来自 https://your-site.com 的请求。” 配置的关键,就在于修改Worker的代码,使其在响应中植入这些头部。

一个基础的CORS处理函数

最直接的方法是在你的Worker脚本中,在处理响应之前或之后,设置一组标准的CORS头部。下面是一个适用于大多数场景的JavaScript示例:

// 定义允许的源,生产环境应替换为具体的域名
const ALLOWED_ORIGIN = 'https://your-site.com';
// 对于需要允许多个源的情况,可以在此进行动态判断
// const ALLOWED_ORIGINS = ['https://site-a.com', 'https://site-b.com'];

// 处理CORS的函数
const handleCORS = (response, request) => {
  // 创建一个新的Response对象,复制原响应的内容
  const newResponse = new Response(response.body, response);

  // 设置CORS头部
  newResponse.headers.set('Access-Control-Allow-Origin', ALLOWED_ORIGIN);
  // 如果需要允许携带Cookie等凭证,则必须指定具体源,不能为'*'
  // newResponse.headers.set('Access-Control-Allow-Credentials', 'true');

  // 明确允许前端使用的请求方法
  newResponse.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  // 明确允许前端在请求中设置的头部
  newResponse.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');

  // 预检请求(OPTIONS)的缓存时间,单位秒
  newResponse.headers.set('Access-Control-Max-Age', '86400');

  return newResponse;
};

// Worker主逻辑
export default {
  async fetch(request, env, ctx) {
    // 处理预检请求(OPTIONS方法)
    if (request.method === 'OPTIONS') {
      // 直接返回一个带有CORS头部的空响应
      return new Response(null, {
        headers: {
          'Access-Control-Allow-Origin': ALLOWED_ORIGIN,
          'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
          'Access-Control-Allow-Headers': 'Content-Type, Authorization',
          'Access-Control-Max-Age': '86400',
        },
      });
    }

    // 你的业务逻辑,例如转发请求到R2
    // const url = new URL(request.url);
    // const objectKey = url.pathname.slice(1);
    // const object = await env.MY_BUCKET.get(objectKey);
    // if (object === null) {
    //   return new Response('Not Found', { status: 404 });
    // }
    // const headers = new Headers();
    // object.writeHttpMetadata(headers);
    // headers.set('etag', object.httpEtag);
    // const response = new Response(object.body, { headers });

    // 假设上面的业务逻辑生成了一个`response`
    // 将response通过CORS处理函数包装后返回
    // return handleCORS(response, request);

    // 此处为演示,返回一个简单响应
    const myResponse = new Response(JSON.stringify({ message: 'Hello from Worker!' }), {
      headers: { 'Content-Type': 'application/json' },
    });
    return handleCORS(myResponse, request);
  },
};

针对R2存储桶的特定配置

如果你的Worker核心功能是代理对Cloudflare R2存储桶的访问,那么CORS配置需要双管齐下。首先,按照上述方法在Worker代码中设置响应头部。其次,R2存储桶本身也需要配置CORS规则。这是因为当Worker代表客户端向R2发起请求时,R2返回的响应如果没有CORS头部,Worker即便添加了头部,也可能遇到深层问题。

通过Cloudflare仪表板或Wrangler CLI为R2存储桶配置CORS规则时,其本质是上传一个CORS策略JSON文档。一个典型的策略如下所示,它允许来自你网站的所有请求方法:

[
  {
    "AllowedOrigins": ["https://your-site.com"],
    "AllowedMethods": ["GET", "POST", "PUT", "DELETE", "HEAD"],
    "AllowedHeaders": ["*"],
    "ExposeHeaders": ["ETag"],
    "MaxAgeSeconds": 3600
  }
]

将桶的CORS规则与Worker的响应头部处理结合,才能构成一个坚固的、无CORS错误的访问链路。有些开发者只在一边配置,结果在复杂的请求(尤其是携带自定义头部的POST请求)上依然碰壁,问题往往就出在这里。

动态源管理与调试技巧

在开发阶段,你可能会在localhost和线上域名之间切换。硬编码单个ALLOWED_ORIGIN会很麻烦。一个更灵活的模式是根据请求的Origin头部动态判断:

const ALLOWED_ORIGINS = ['https://your-site.com', 'http://localhost:3000', 'http://127.0.0.1:5500'];

const origin = request.headers.get('Origin');
if (ALLOWED_ORIGINS.includes(origin)) {
  newResponse.headers.set('Access-Control-Allow-Origin', origin);
}
// 注意:如果设置了具体的Origin,通常可以同时设置
// newResponse.headers.set('Access-Control-Allow-Credentials', 'true');

当CORS错误仍然出现时,别慌。打开浏览器开发者工具的“网络”选项卡,仔细查看出错请求的详细信息。重点关注:1) 是“预检”请求(OPTIONS)失败了,还是实际请求(GET/POST)失败了?2) 服务器返回的响应头部里,Access-Control-Allow-*系列头部是否正确、完整?对比你的Worker代码逻辑,缺口往往一目了然。

说到底,配置Cloudflare Workers避免CORS错误,不是一个“开关”,而是一套符合规范的“对话”协议。让你的Worker学会用浏览器能理解的安全语言进行回应,后面的数据传输自然就畅通无阻了。

5 条回复 A文章作者 M管理员
  1. 牛油果果

    搞了半天原来是R2桶也要配CORS,之前只改了Worker难怪不行。

  2. 摇摆时光

    这个动态判断源的方法挺实用,本地调试不用来回改代码了。

  3. 逆刃

    Allow-Credentials设了true之后,带cookie的请求还会报错吗?

  4. DreadFang

    代码示例可以直接用,已部署测试通过,感谢分享!

  5. 憨不愣

    之前也卡在预检请求这块,看了网络面板才发现OPTIONS没返回头。

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索