You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将CORS检查应用迁移至Cloudflare Workers时的Header缺失问题咨询

问题描述

我正尝试将CORS检查应用从Heroku迁移至Cloudflare Workers,该应用原本使用axios向目标网站发送OPTIONS请求,检测是否存在x-frame-options Header,并根据其值返回布尔结果。迁移后功能无法正常运行,我是否可以认为Cloudflare会剥离这些Header,导致该功能无法在Cloudflare Workers上实现?

// node_modules/itty-router/dist/itty-router.mjs
var e = ({ base: e2 = "", routes: r = [] } = {}) => ({ __proto__: new Proxy({}, { get: (a, o, t) => (a2, ...p) => r.push([o.toUpperCase(), RegExp(`^${(e2 + a2).replace(/(\/?)\*/g, "($1.*)?").replace(/(\/$)|((?<=\/)\/)/, "").replace(/:(\w+)\+/, "(?<$2>.*)").replace(/:(\w+)(\?)?(\.)?/g, "$2(?<$1>[^/]+)$2$3").replace(/\.(?=[\w(])/, "\\.").replace(/\)\.\?\(([^\[]+)\[\^/g, "?)\\.?($1(?<=\\.)[^\\.&quot;)}/*$`), p]) && t }), routes: r, async handle(e3, ...a) {
  let o, t, p = new URL(e3.url), l = e3.query = {};
  for (let [e4, r2] of p.searchParams)
    l[e4] = void 0 === l[e4] ? r2 : [l[e4], r2].flat();
  for (let [l2, s, c] of r)
    if ((l2 === e3.method || "ALL" === l2) && (t = p.pathname.match(s))) {
      e3.params = t.groups || {};
      for (let r2 of c)
        if (void 0 !== (o = await r2(e3.proxy || e3, ...a)))
          return o;
    }
} });

// src/index.ts
var router = e();
var corsHeaders = {
  "Access-Control-Allow-Origin": "*",
  "Access-Control-Allow-Methods": "GET,HEAD,POST,OPTIONS",
  "Access-Control-Max-Age": "86400"
};
function jsonResponse(body) {
  return new Response(JSON.stringify(body), {
    status: 200,
    headers: {
      "Content-Type": "application/json",
      "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload"
    }
  });
}
function handleOptions(request) {
  let headers = request.headers;
  if (headers.get("Origin") !== null && headers.get("Access-Control-Request-Method") !== null && headers.get("Access-Control-Request-Headers") !== null) {
    let respHeaders = {
      ...corsHeaders,
      "Access-Control-Allow-Headers": request.headers.get("Access-Control-Request-Headers")
    };
    return new Response(null, {
      headers: respHeaders
    });
  } else {
    return new Response(null, {
      headers: {
        Allow: "GET, HEAD, POST, OPTIONS"
      }
    });
  }
}
router.options("/test-cors", async function(request) {
  return handleOptions(request);
});
router.post("/test-cors", async function(request) {
  const url = request.body.url;
  const externalRequest = new Request(url, { method: "head" });
  try {
    let response = await fetch(externalRequest);
    if (response.headers.get("x-frame-options") !== null) {
      const frameOptionsValue = response.headers.get("x-frame-options");
      if (frameOptionsValue === "SAMEORIGIN") {
        return jsonResponse({ canAccess: false });
      } else {
        return jsonResponse({ canAccess: true });
      }
    } else {
      return jsonResponse({ canAccess: true });
    }
  } catch (error) {
    console.error(error);
    if (error.code === "ENOTFOUND" || error.response.status === 404) {
      return jsonResponse({ canAccess: false, reason: "NOT_FOUND" });
    } else {
      return jsonResponse({ canAccess: false });
    }
  }
});
router.all("*", (request, args) => {
  return new Response("Not Found", {
    status: 404,
    headers: {
      "Content-Type": "text/html; charset=utf-8",
      "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload"
    }
  });
});
var src_default = {
  async fetch(request, env, ctx) {
    return router.handle(request);
  }
};
export {
  src_default as default
};
//# sourceMappingURL=index.js.map
解答

Cloudflare Workers 不会主动剥离x-frame-options这类响应头,问题出在你的代码逻辑上:

  • 请求方法不匹配:原Heroku应用使用OPTIONS请求检测,但迁移后的Worker代码里发送的是HEAD请求。部分网站对不同请求方法返回的响应头可能不一致,比如有些网站仅在GET/HEAD请求返回x-frame-options,但也有部分网站只会针对浏览器发起的请求返回该头,或者对非浏览器请求(比如Worker的fetch)隐藏。

  • 请求体解析问题:你直接从request.body.url获取目标URL,但在Cloudflare Workers中,request.body是一个ReadableStream,不能直接读取属性,需要先解析请求体:

    // 修正后的POST处理逻辑
    router.post("/test-cors", async function(request) {
      const body = await request.json(); // 先解析JSON请求体
      const url = body.url;
      // ... 后续逻辑
    });
    
  • 错误处理逻辑问题:error.response在Worker的fetch抛出的错误中不存在,因为fetch仅在网络层面失败时抛出错误(比如DNS解析失败),HTTP错误(如404)不会抛出,而是返回ok: false的Response对象。需要调整错误处理:

    try {
      let response = await fetch(externalRequest);
      if (!response.ok) {
        // 处理HTTP错误,比如404
        return jsonResponse({ canAccess: false, reason: "HTTP_ERROR", status: response.status });
      }
      // ... 检测x-frame-options的逻辑
    } catch (error) {
      console.error(error);
      // 仅处理网络错误
      return jsonResponse({ canAccess: false, reason: "NETWORK_ERROR" });
    }
    
  • 请求头模拟:部分网站会根据请求的User-Agent等头判断是否返回x-frame-options,可以模拟浏览器请求头提高准确性:

    const externalRequest = new Request(url, {
      method: "HEAD",
      headers: {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
      }
    });
    

总结:Cloudflare Workers本身不会影响你获取响应头,核心问题是代码中的请求方法、请求体解析、错误处理逻辑与原应用不一致,以及部分网站对请求来源的判断导致响应头缺失。调整上述几点后功能应该可以正常运行。


内容的提问来源于stack exchange,提问作者Bartlomiej Lewandowski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 22:32:22