Skip to content

Instantly share code, notes, and snippets.

@dexit
Created November 4, 2025 13:02
Show Gist options
  • Select an option

  • Save dexit/eb76d31d1e07d7468d21f6faaec30db4 to your computer and use it in GitHub Desktop.

Select an option

Save dexit/eb76d31d1e07d7468d21f6faaec30db4 to your computer and use it in GitHub Desktop.
CF Worker Cors Proxy Preflight HEaders Allowed whitelist domains with wildcard support and endpoint url control
// --- CONFIGURATION VIA ENVIRONMENT VARIABLES ---
// In your Worker's Settings -> Variables, set the following:
//
// 1. ALLOWED_ORIGINS (Whitelist for target URLs)
// Value: *.example.com, api.another.com, my-domain.net
//
// 2. PROXY_ENDPOINT (The path the proxy will run on)
// Value: /corsproxy/
// A list of headers to remove from the origin's response.
const HEADERS_TO_STRIP = [
'content-security-policy',
'x-frame-options',
'x-content-type-options',
];
/**
* Checks if a given URL's hostname is allowed based on a whitelist.
* @param {string} targetUrl The URL whose hostname needs to be checked.
* @param {string} allowedOriginsCommaSeparated A comma-separated string of allowed domains.
* @returns {boolean} True if the URL is allowed, false otherwise.
*/
function isUrlAllowed(targetUrl, allowedOriginsCommaSeparated) {
if (!allowedOriginsCommaSeparated) return false; // Deny if not configured
const whitelist = allowedOriginsCommaSeparated.split(',').map(domain => domain.trim());
const hostname = new URL(targetUrl).hostname;
for (const pattern of whitelist) {
if (pattern === '*') return true;
if (pattern.startsWith('*.')) {
const domain = pattern.substring(2);
if (hostname.endsWith('.' + domain) || hostname === domain) return true;
} else {
if (hostname === pattern) return true;
}
}
return false;
}
async function handleOptions(request) {
const corsHeaders = {
"Access-Control-Allow-Origin": "*", // More permissive for preflight
"Access-Control-Allow-Methods": "GET, HEAD, POST, PUT, DELETE, OPTIONS",
"Access-Control-Max-Age": "86400",
};
if (
request.headers.get("Origin") !== null &&
request.headers.get("Access-Control-Request-Method") !== null &&
request.headers.get("Access-Control-Request-Headers") !== null
) {
// Handle CORS preflight requests.
return new Response(null, {
headers: {
...corsHeaders,
"Access-Control-Allow-Headers": request.headers.get("Access-Control-Request-Headers"),
},
});
} else {
// Handle standard OPTIONS request.
return new Response(null, {
headers: { Allow: "GET, HEAD, POST, PUT, DELETE, OPTIONS" },
});
}
}
async function handleRequest(request, allowedOrigins) {
const url = new URL(request.url);
const targetUrl = url.searchParams.get("apiurl");
if (!targetUrl) {
return new Response('Error: "apiurl" query parameter is missing.', { status: 400 });
}
// --- Whitelist Check ---
if (!isUrlAllowed(targetUrl, allowedOrigins)) {
return new Response(`Error: The requested URL is not in the whitelist.`, { status: 403 });
}
// Rewrite request to point to the target URL.
const forwardedRequest = new Request(targetUrl, request);
forwardedRequest.headers.set("Origin", new URL(targetUrl).origin);
let response = await fetch(forwardedRequest);
response = new Response(response.body, response);
// Set secure CORS headers on the final response.
response.headers.set("Access-Control-Allow-Origin", url.origin);
response.headers.append("Vary", "Origin");
HEADERS_TO_STRIP.forEach(header => {
response.headers.delete(header);
});
return response;
}
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
const proxyEndpoint = env.PROXY_ENDPOINT || "/corsproxy/"; // Default to /corsproxy/
if (url.pathname.startsWith(proxyEndpoint)) {
if (request.method === "OPTIONS") {
return handleOptions(request);
} else if (["GET", "HEAD", "POST", "PUT", "DELETE"].includes(request.method)) {
return handleRequest(request, env.ALLOWED_ORIGINS || "pathwaygroup.co.uk,pathwayskillzone.ac.uk");
} else {
return new Response(null, { status: 405, statusText: "Method Not Allowed" });
}
} else {
// Serve a demo page for any other path
return new Response("This worker is running. Use the configured proxy endpoint to make requests.", {
headers: { "Content-Type": "text/plain" },
});
}
},
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment