CORS on Cloudflare Workers — Headers Without a Backend
Cloudflare Workers run JavaScript at the edge — no traditional server, no nginx.conf. Add CORS headers directly in your Worker's fetch handler.
Basic CORS in a Worker
export default {
async fetch(request) {
// Handle OPTIONS preflight
if (request.method === 'OPTIONS') {
return new Response(null, {
status: 204,
headers: {
'Access-Control-Allow-Origin': 'https://yourapp.com',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Max-Age': '86400',
},
});
}
// Your actual response
const data = { status: 'ok' };
return new Response(JSON.stringify(data), {
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': 'https://yourapp.com',
'Access-Control-Allow-Credentials': 'true',
},
});
},
};
Dynamic origin allowlist
const ALLOWED_ORIGINS = ['https://yourapp.com', 'https://staging.yourapp.com'];
function corsHeaders(origin) {
const allowed = ALLOWED_ORIGINS.includes(origin) ? origin : '';
return {
'Access-Control-Allow-Origin': allowed,
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Allow-Credentials': 'true',
'Vary': 'Origin',
};
}
export default {
async fetch(request) {
const origin = request.headers.get('Origin') || '';
if (request.method === 'OPTIONS') {
return new Response(null, { status: 204, headers: corsHeaders(origin) });
}
return new Response(JSON.stringify({ ok: true }), {
headers: { 'Content-Type': 'application/json', ...corsHeaders(origin) },
});
},
};
Using Hono framework
import { Hono } from 'hono';
import { cors } from 'hono/cors';
const app = new Hono();
app.use('/api/*', cors({
origin: 'https://yourapp.com',
allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],
allowHeaders: ['Content-Type', 'Authorization'],
credentials: true,
}));
app.get('/api/data', (c) => c.json({ data: 'ok' }));
export default app;
Test your Worker CORS config →