Deploying a Cloudflare Worker to Forward Traffic to a REST API¶
This guide explains how to create and deploy a Cloudflare Worker that listens for incoming POST requests containing a uuid
, enriches the request with metadata, and forwards it to a specified backend REST API.
What You’ll Do¶
- Create a Cloudflare Worker using the dashboard UI.
- Replace the default code with a custom forwarding script.
- Deploy the Worker to production.
Step 1: Log in to Cloudflare¶
- Visit https://dash.cloudflare.com/
- Log in using your Cloudflare account credentials.
Step 2: Navigate to Workers & Pages¶
- In the left-hand sidebar, click Workers & Pages.
- Click the Create application button.
Step 3: Choose Create Worker¶
- Select the Worker tab (not Pages).
- Click Create Worker to proceed.
Step 4: Replace the Default Code¶
Once you're in the online editor:
1. Delete the sample code in the editor.
2. Paste the custom Cloudflare Worker script (provided below).
Text Only
3. Update the following variables in the script:
addEventListener("fetch", event => {
event.respondWith(handleRequest(event));
});
const config = {
restProxyUrl: "http://rest-proxy.netfein.com:8084/topics/test-claudflare",
apiCatalogId: "fdbcbf71-af18-4887-98fa-3bc670754455",
source: "claudflare"
};
async function handleRequest(event) {
const request = event.request;
try {
const reqClone = request.clone();
const url = new URL(request.url);
const method = request.method;
const ip = request.headers.get("CF-Connecting-IP") || "unknown";
const time = Math.floor(Date.now() / 1000).toString();
const path = url.pathname;
const requestHeaders = extractHeaders(reqClone.headers);
const requestBody = await readTruncatedBody(reqClone);
const response = await fetch(request);
const responseClone = response.clone();
const statusCode = response.status;
const status = response.statusText;
const contentType = responseClone.headers.get("Content-Type") || "";
const responseHeaders = extractHeaders(responseClone.headers);
const responseBody = await readTruncatedBody(responseClone);
const payload = {
path,
ip,
time,
statusCode,
type: contentType,
status,
apiCatalogId: config.apiCatalogId,
source: config.source,
method,
requestHeaders: JSON.stringify(requestHeaders),
responseHeaders: JSON.stringify(responseHeaders),
requestPayload: requestBody,
responsePayload: responseBody
};
event.waitUntil(
(async () => {
try {
await sendToRestProxy(payload);
} catch (err) {
console.error("Failed to send to REST proxy:", err.message);
}
})()
);
return response;
} catch (err) {
console.error("Request handling error:", err.message);
return genErrorPage(err);
}
}
function extractHeaders(headers) {
const result = {};
for (const [key, value] of headers.entries()) {
result[key] = value;
}
return result;
}
async function readTruncatedBody(stream) {
try {
const text = await stream.text();
return text.length > 10000 ? text.slice(0, 10000) + "...(truncated)" : text;
} catch (e) {
console.warn("Body read error:", e.message);
return "";
}
}
async function sendToRestProxy(payload) {
const body = {
records: [{ value: payload }]
};
// Timeout controller setup
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 3000); // 3 seconds timeout
try {
const response = await fetch(config.restProxyUrl, {
method: "POST",
headers: {
"Content-Type": "application/vnd.kafka.json.v2+json",
"Accept": "application/vnd.kafka.v2+json"
},
body: JSON.stringify(body),
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok) {
const errorText = await response.text();
console.error(`REST proxy error ${response.status}: ${errorText}`);
return;
}
} catch (err) {
clearTimeout(timeoutId);
console.error("REST proxy fetch failed:", err.message);
return;
}
}
function genErrorPage(err) {
const message = err?.message || "Unknown error";
const stack = err?.stack || "";
const html = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>500 Internal Server Error</title>
<style>
body { font-family: Arial, sans-serif; background: #f2f2f2; color: #333; padding: 2em; }
h1 { color: #c00; }
pre { background: #eee; padding: 1em; border-radius: 5px; overflow-x: auto; }
</style>
</head>
<body>
<h1>500 Internal Server Error</h1>
<p><strong>Error:</strong> ${escapeHtml(message)}</p>
<pre>${escapeHtml(stack)}</pre>
</body>
</html>
`;
return new Response(html, {
status: 500,
headers: { "Content-Type": "text/html; charset=utf-8" }
});
}
function escapeHtml(text) {
return String(text)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
-
restProxyUrl
: The URL of the REST API endpoint that forwards data to the relevant Kafka topic. -
apiCatalogId
: The application ID associated with the domain, defined earlier in ApiFort.
Step 5: Save and Deploy¶
- Click the Save and Deploy button in the top-right corner of the editor.