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