Cloudflare Workers CORS 跨域问题排查与解决

Cloudflare Workers CORS 跨域问题排查与解决表单失败的根因是:Worker 没有正确处理 CORS 预检请求(OPTIONS)。只要把 CORS + OPTIONS 补齐,fetch failed 会立刻消失。

日志逐条解释

这个 CSP 报错【不是你的问题】

text 复制代码
Executing inline script violates the following Content Security Policy...
chrome-extension://a05f635f...

这是浏览器扩展(插件)注入脚本被 CSP 拦截。

  • 来自 chrome-extension://...
  • 和你的 Astro / Worker / 表单 无关
  • 不影响接口请求

可以完全忽略。

这个才是致命错误【真正原因】

text 复制代码
Access to fetch at 'https://xxx.workers.dev/api/contact'
from origin 'http://localhost:4321'
has been blocked by CORS policy:

Response to preflight request doesn't pass access control check:
No 'Access-Control-Allow-Origin' header

关键信息:

  • 浏览器发起了 OPTIONS 预检请求
  • Worker 没有返回 CORS 头
  • 浏览器直接拦截 → ERR_FAILED

为什么会有 OPTIONS 预检?

因为你满足了以下任意条件(你全中了):

  • Content-Type: application/json
  • POST 请求
  • 跨域(localhost → workers.dev)

浏览器 一定会先发 OPTIONS。

你现在的 Worker 一定缺了这段逻辑

js 复制代码
if (request.method === 'OPTIONS') {
  // 你现在没有正确处理
}

直接给你一份生产可用的 Worker 模板

你可以整体替换当前 Worker 代码。

js 复制代码
const corsHeaders = {
  'Access-Control-Allow-Origin': 'http://localhost:4321', // 本地开发
  // 生产环境可以换成你的域名
  // 'https://yourdomain.com'

  'Access-Control-Allow-Methods': 'POST, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type',
}

export default {
  async fetch(request, env) {
    // 1️⃣ 处理 CORS 预检
    if (request.method === 'OPTIONS') {
      return new Response(null, {
        status: 204,
        headers: corsHeaders,
      })
    }

    // 2️⃣ 只允许 POST
    if (request.method !== 'POST') {
      return new Response('Method Not Allowed', {
        status: 405,
        headers: corsHeaders,
      })
    }

    // 3️⃣ 解析数据
    const { name, email, phone, message } = await request.json()

    if (!name || !email || !message) {
      return new Response(
        JSON.stringify({ error: 'Missing fields' }),
        {
          status: 400,
          headers: {
            ...corsHeaders,
            'Content-Type': 'application/json',
          },
        }
      )
    }

    // 4️⃣ 调用 Resend
    const res = await fetch('https://api.resend.com/emails', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${env.RESEND_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        from: 'Website Contact <no-reply@YOUR_VERIFIED_DOMAIN.com>',
        to: ['your@email.com'],
        subject: 'New Contact Message',
        html: `
          <p><b>Name:</b> ${name}</p>
          <p><b>Email:</b> ${email}</p>
          <p><b>Phone:</b> ${phone || '-'}</p>
          <p><b>Message:</b> ${message}</p>
        `,
      }),
    })

    if (!res.ok) {
      const err = await res.text()
      return new Response(err, {
        status: 500,
        headers: corsHeaders,
      })
    }

    // 5️⃣ 成功返回
    return new Response(
      JSON.stringify({ success: true }),
      {
        headers: {
          ...corsHeaders,
          'Content-Type': 'application/json',
        },
      }
    )
  },
}

Astro 前端这边你不用改(基本是对的)

js 复制代码
await fetch('https://icy-truth-83e1.bennyxqg.workers.dev/api/contact', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(formData),
})

没问题,CORS 是后端的责任。

常见优化(等你跑通后再做)

允许多个来源(开发 + 生产)

js 复制代码
const origin = request.headers.get('Origin')
const allowed = ['http://localhost:4321', 'https://yourdomain.com']

if (allowed.includes(origin)) {
  corsHeaders['Access-Control-Allow-Origin'] = origin
}

临时调试(不推荐生产)

js 复制代码
'Access-Control-Allow-Origin': '*'
相关推荐
Maynor9964 天前
两个人、一支话筒、一间橙色录音棚:Hotel Lobby AI(LobbyDuo)双人视频生成器分享
短视频·cloudflare·stripe·ai视频生成·seedance·双人视频
cpolar技术支持12 天前
Spring Boot 接口本地正常,异地前端却报跨域?用 cpolar 跑通 CORS 预检与白名单
java·springboot·cpolar·前后端分离·cors
JackSparrow41413 天前
和AI一起将全部CSDN博文迁移到个人博客站
人工智能·程序人生·ai·github·cloudflare·astro·静态博客
老王生涯19 天前
针对cloudflare防护的爬虫策略——CDP
爬虫·cloudflare·cdp·反爬
csdn_aspnet1 个月前
.NetCore Webapi 接口验证请求来源
.netcore·ip·token·签名·webapi·cors·sign
zander2581 个月前
知识分享平台项目复习第二天:登录模块中的双令牌与刷新安全(2)
xss·csrf·cors·登录模块·cookie的三个属性
Java成神之路-1 个月前
浏览器同源策略与 CORS 跨域详解
cors
boke112百科1 个月前
怎么将Cloudflare Pages子域名301跳转到自定义域名?
运维·cloudflare·静态网站·301重定向
請你喝杯Java1 个月前
网站部署平台怎么选:Vercel、Cloudflare、ChatGPT Sites 的区别、费用与国内访问
chatgpt·cloudflare·vercel