Last active
July 9, 2025 16:32
-
-
Save navetacandra/c5c709920b7d7c4105a7fee34ae0a46f to your computer and use it in GitHub Desktop.
CORS Proxy Cloudflare Worker
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const OMIT_REQUEST_HEADERS = [ | |
'cookie', | |
'cookie2', | |
'x-request-start', | |
'x-request-id', | |
'via', | |
'connect-time', | |
'total-route-time' | |
]; | |
const ALLOWED_ORIGINS = [ | |
"example.com", | |
/^localhost(:\d+)?$/, | |
/^[\s\S]+\.cf-username\.workers\.dev$/i, | |
// ... | |
]; | |
const ALLOWED_METHODS = ['GET', 'POST', 'HEAD', 'OPTIONS']; | |
const PROXY_ENDPOINT = "/request"; | |
function isOriginAllowed(origin) { | |
return ALLOWED_ORIGINS.some(allowed => | |
allowed instanceof RegExp | |
? allowed.test(origin) | |
: allowed === origin | |
); | |
} | |
function createProxyRequest(originalRequest, targetUrl) { | |
const newHeaders = new Headers(originalRequest.headers); | |
// Remove sensitive headers | |
OMIT_REQUEST_HEADERS.forEach(header => newHeaders.delete(header)); | |
// Set new Origin header | |
newHeaders.set('Origin', new URL(targetUrl).hostname); | |
return new Request(targetUrl, { | |
method: originalRequest.method, | |
headers: newHeaders, | |
body: originalRequest.body, | |
redirect: 'follow' | |
}); | |
} | |
function handleOptions(request) { | |
return new Response(null, { | |
headers: { | |
'Access-Control-Allow-Origin': request.headers.get('Origin') || '*', | |
'Access-Control-Allow-Methods': ALLOWED_METHODS.join(', '), | |
'Access-Control-Allow-Headers': request.headers.get('Access-Control-Request-Headers') || '', | |
'Access-Control-Expose-Headers': '*', | |
'Access-Control-Max-Age': '86400', | |
} | |
}); | |
} | |
export default { | |
async fetch(request, env, ctx) { | |
const { method } = request; | |
const url = new URL(request.url); | |
const origin = request.headers.get('Origin') || ''; | |
const originHostname = new URL(origin).hostname; | |
// Handle OPTIONS preflight requests | |
if (method === 'OPTIONS') { | |
return handleOptions(request); | |
} | |
// Validate HTTP method | |
if (!ALLOWED_METHODS.includes(method)) { | |
return new Response(`Method ${method} not allowed`, { | |
status: 405, | |
headers: { 'Allow': ALLOWED_METHODS.join(', ') } | |
}); | |
} | |
// Check origin against whitelist | |
if (!isOriginAllowed(originHostname)) { | |
return new Response('Forbidden', { status: 403 }); | |
} | |
// Non-proxy endpoint handling | |
if (url.pathname !== PROXY_ENDPOINT) { | |
return new Response('', { | |
status: 200, | |
headers: { | |
'Content-Type': 'text/plain', | |
'Access-Control-Allow-Origin': origin | |
} | |
}); | |
} | |
try { | |
// Validate and decode target URL | |
const targetUrl = url.searchParams.get('url'); | |
if (!targetUrl) { | |
throw new Error('Missing target URL'); | |
} | |
const decodedUrl = decodeURIComponent(targetUrl); | |
const targetUrlObj = new URL(decodedUrl); | |
// Prevent proxy recursion | |
if (targetUrlObj.hostname === url.hostname && targetUrlObj.pathname === PROXY_ENDPOINT) { | |
throw new Error('Self-proxying disallowed'); | |
} | |
// Create and execute proxy request | |
const proxyRequest = createProxyRequest(request, decodedUrl); | |
const response = await fetch(proxyRequest); | |
// Create CORS-enabled response | |
const corsResponse = new Response(response.body, response); | |
corsResponse.headers.set('Access-Control-Allow-Origin', origin); | |
corsResponse.headers.set('Access-Control-Expose-Headers', '*'); | |
corsResponse.headers.set( | |
'Content-Security-Policy', | |
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src *; font-src 'self'; object-src 'none'; media-src 'self';" | |
); | |
return corsResponse; | |
} catch (error) { | |
console.error('Proxy error:', error); | |
return new Response('Bad Request', { | |
status: 400, | |
headers: { 'Access-Control-Allow-Origin': origin } | |
}); | |
} | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment