CORS

Why Your Cloudflare CORS Still Fails — And How to Fix It

Updated April 2026

Reading this? Verify your fix live. Test your CORS config → CORSFixer

You added CORS headers to your server. They work when you hit the origin directly. They fail through Cloudflare. These are the four Cloudflare-specific CORS problems and their exact fixes.

Problem 1 — Cloudflare is caching a stale response without CORS headers

# Symptom: CORS works in curl to origin, fails in browser through Cloudflare
# Cause: Cloudflare cached the response before CORS headers were added

# Fix: Purge Cloudflare cache
# Dashboard → Caching → Configuration → Purge Cache → Purge Everything

# Or purge specific URLs via API
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/purge_cache" -H "Authorization: Bearer YOUR_TOKEN" -H "Content-Type: application/json" --data '{"purge_everything": true}'

Problem 2 — Cloudflare is caching OPTIONS preflight responses

# Symptom: OPTIONS returns correct CORS headers once, then cached incorrectly
# Fix: Tell Cloudflare not to cache preflight responses

# On your origin server — add to OPTIONS responses:
Cache-Control: no-store

# Or in Cloudflare — create a Cache Rule:
# Rules → Cache Rules → Create Rule
# If: Request method equals "OPTIONS"
# Then: Bypass cache

Problem 3 — Duplicate CORS headers (origin + Transform Rule)

# Symptom: "The 'Access-Control-Allow-Origin' header contains multiple values"
# Cause: Both your origin server AND Cloudflare Transform Rule add the header

# Fix Option A: Remove from origin, add only via Transform Rule
# Transform Rules → Modify Response Headers → Set (overwrite) not Add

# Fix Option B: Remove from Transform Rule, handle only at origin
# Dashboard → Rules → Transform Rules → delete the CORS rule

Problem 4 — Cloudflare stripping custom headers

# Symptom: CORS headers from your origin disappear when proxied through Cloudflare
# This is uncommon but can happen with certain header names

# Check: Does your header appear in curl to origin?
curl -I https://your-origin-ip.com/ -H "Host: yourapp.com"

# If yes but not through Cloudflare: create a Transform Rule to re-add them
# Rules → Transform Rules → Modify Response Headers
# Action: Set | Header name: Access-Control-Allow-Origin | Value: https://yourapp.com

Adding CORS via Cloudflare Transform Rules (the clean approach)

# Dashboard → Rules → Transform Rules → Modify Response Headers → Create Rule

# Rule name: CORS Headers
# When: Hostname equals yourapp.com
# Then:
# Set Access-Control-Allow-Origin = https://yourfrontend.com
# Set Access-Control-Allow-Methods = GET, POST, PUT, DELETE, OPTIONS
# Set Access-Control-Allow-Headers = Authorization, Content-Type

# Note: Set overwrites; Add appends. Use Set to avoid duplicates.

Handle OPTIONS preflight at Cloudflare Worker (most reliable)

// workers.js — handles CORS before request reaches your origin
export default { async fetch(request, env) { const origin = request.headers.get("Origin"); const ALLOWED = ["https://yourapp.com"]; const allowedOrigin = ALLOWED.includes(origin) ? origin : ""; if (request.method === "OPTIONS") { return new Response(null, { status: 204, headers: { "Access-Control-Allow-Origin": allowedOrigin, "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS", "Access-Control-Allow-Headers": "Authorization, Content-Type", "Access-Control-Max-Age": "86400", "Cache-Control": "no-store", }, }); } const response = await fetch(request); const newResponse = new Response(response.body, response); newResponse.headers.set("Access-Control-Allow-Origin", allowedOrigin); return newResponse; },
};
Test your CORS config → CORSFixer