Fix CORS Errors on Cloudflare
When Cloudflare sits in front of your origin, CORS headers set at the origin may not match what the browser sees — Cloudflare can cache responses, modify headers, or block OPTIONS preflights. Fix CORS at the edge for reliable results.
Option 1 — Transform Rules (no Workers required)
Dashboard → Rules → Transform Rules → Modify Response Header → Create Rule:
When: Custom filter expression (http.request.uri.path wildcard "/api/*")
Then — Set static: Access-Control-Allow-Origin → https://your-frontend.com Access-Control-Allow-Methods → GET, POST, PUT, DELETE, OPTIONS Access-Control-Allow-Headers → Content-Type, Authorization Access-Control-Max-Age → 86400 Vary → Origin
For OPTIONS preflight, create a second rule that returns 204:
When: (http.request.method eq "OPTIONS") and (http.request.uri.path wildcard "/api/*")
Then — Block (returns 204 with CORS headers above)
Option 2 — Cloudflare Worker
const ALLOWED_ORIGINS = ['https://your-frontend.com'];
export default { async fetch(request, env) { const origin = request.headers.get('Origin') || ''; const isAllowed = ALLOWED_ORIGINS.includes(origin); const corsHeaders = isAllowed ? { 'Access-Control-Allow-Origin': origin, 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, Authorization', 'Access-Control-Max-Age': '86400', 'Vary': 'Origin', } : {}; // Handle preflight if (request.method === 'OPTIONS') { return new Response(null, { status: 204, headers: corsHeaders }); } // Proxy to origin and add CORS headers const response = await fetch(request); const newResponse = new Response(response.body, response); Object.entries(corsHeaders).forEach(([k, v]) => newResponse.headers.set(k, v)); return newResponse; }
}
Why Cloudflare caching breaks CORS
If Cloudflare caches a response without Vary: Origin, it may serve the cached response with the wrong Access-Control-Allow-Origin to a different origin. Always set Vary: Origin when using dynamic CORS.