Skip to main content

CDN integration

Use the check-request API at the edge (e.g. a CDN Worker) to get allow, challenge, or block for each request before traffic reaches your origin. You integrate by calling the API from your Worker or edge code.


Cloudflare Workers

Client IP

Use the real client IP, not the Worker’s IP. Prefer Cloudflare’s connecting IP, then fall back to the leftmost entry in X-Forwarded-For:

  • request.cf?.connectingIp — preferred on Cloudflare (the client IP Cloudflare sees).
  • X-Forwarded-For — if you need a fallback, use the leftmost (client) value: request.headers.get('x-forwarded-for')?.split(',')[0]?.trim().

Never send the Worker’s own IP to the check-request API.

Minimal Worker example

Call POST /api/v1/check-request with your secret in the Authorization header and a JSON body. Use a short timeout (e.g. 400 ms) and fail-open (on timeout or error, treat as allow).

API base URL: https://api.trustedaccounts.org (or your environment’s base; the endpoint is /api/v1/check-request).

// --- Configuration (use env vars or Worker secrets in production) ---
const API_BASE = 'https://api.trustedaccounts.org';
const CHECK_REQUEST_TIMEOUT_MS = 400;

export default {
async fetch(request, env) {
const url = new URL(request.url);

// 1) Get the real client IP (never use the Worker’s own IP)
const ip = request.cf?.connectingIp ?? request.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ?? '';
if (!ip) return fetch(request); // no IP: pass through without checking

// 2) Parse session/visitor ID from cookie (used for linking challenges and avoiding repeated prompts)
const cookieHeader = request.headers.get('cookie') ?? '';
const trustedId = getTrustedIdFromCookie(cookieHeader);
const requestedPath = url.pathname + url.search;

// 3) Build the request body for the check-request API (ip required; trustedId and path optional)
const body = {
ip,
requestedPath,
...(trustedId && { trustedId }),
};

let checkResponse = { decision: 'allow' };
try {
// 4) Call the check-request API with a short timeout; abort and fail-open if it’s slow
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), CHECK_REQUEST_TIMEOUT_MS);
const apiRes = await fetch(`${API_BASE}/api/v1/check-request`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${env.TRUSTED_SECRET_KEY}`,
},
body: JSON.stringify(body),
signal: controller.signal,
});
clearTimeout(timeoutId);

if (!apiRes.ok) return fetch(request); // API error: fail-open and allow the request

const setCookie = apiRes.headers.get('set-cookie');
checkResponse = await apiRes.json();

// 5) Allow: forward to origin and pass through any Set-Cookie from the API (e.g. new trusted_id)
if (checkResponse.decision === 'allow') {
const response = await fetch(request);
if (setCookie) {
const newHeaders = new Headers(response.headers);
newHeaders.append('set-cookie', setCookie);
return new Response(response.body, { status: response.status, headers: newHeaders });
}
return response;
}

// 6) Challenge or block: redirect to our hosted challenge or blocked page
const sessionId = checkResponse.sessionId ?? trustedId ?? '';
const redirectUrl = url.origin + requestedPath; // where to send the user after they complete the challenge
const params = new URLSearchParams({
...(sessionId && { trusted_id: sessionId }),
publishable_key: env.TRUSTED_PUBLISHABLE_KEY,
redirect_url: redirectUrl,
});
const location = checkResponse.decision === 'block'
? `https://blocked.trustedaccounts.org/blocked.html?${params}`
: `https://challenge.trustedaccounts.org/challenge.html?${params}`;
return Response.redirect(location, 302);
} catch (_) {
return fetch(request); // timeout or network error: fail-open and allow the request
}
},
};

// Extract the trusted_id cookie value (session/visitor ID) from the Cookie header
function getTrustedIdFromCookie(cookieHeader) {
const m = cookieHeader.match(/\btrusted_id=([^;]*)/);
if (!m) return '';
try {
return decodeURIComponent(m[1].trim());
} catch {
return '';
}
}

Store your secret key and publishable key as Cloudflare Worker secrets (e.g. TRUSTED_SECRET_KEY, TRUSTED_PUBLISHABLE_KEY). Do not hardcode them.

Cookies and headers

  • Cookie / trustedId: Parse the trusted_id cookie from the Cookie header and send it as the body field trustedId. You may also send cookies as a list of cookie names only (no values) for stronger detection; see Bot Protection.
  • Set-Cookie: If the API response includes a Set-Cookie header, forward it to the client (e.g. copy it onto the response you return) so the visitor gets the trusted_id cookie.

Challenge and block

On challenge or block, redirect the user to our hosted pages with the same query params as in Bot Protection:

  • trusted_id — from the visitor’s cookie or the API response’s sessionId.
  • publishable_key — your project’s publishable key.
  • redirect_url — full URL to send the user back to after the challenge (e.g. your site + the path they requested).
PageURL
Challengehttps://challenge.trustedaccounts.org/challenge.html
Blockedhttps://blocked.trustedaccounts.org/blocked.html

For the full request/response schema, optional headers, and cookie rules, see Bot Protection.

Caching

The Worker runs on each incoming request before Cloudflare’s cache. Requests are evaluated by the check-request API before you serve cached or origin content.

FAQ (CDN integration)

Is this scalable at high traffic (e.g. millions of requests per hour)?
Yes. CDN Workers (e.g. Cloudflare) run at the edge and scale out with your traffic. Millions of requests per hour are within normal design limits; the Worker forwards each request to the check-request API and then either serves origin, redirects to challenge/block, or fails open.

Will the 400 ms timeout slow down my site?
No. 400 ms is a timeout ceiling — the maximum time the Worker waits before failing open. The check-request API is built for low latency and usually responds in tens of milliseconds. Most requests get a decision and proceed in well under 100 ms.

What happens if the check-request API is slow or down?
The Worker fails open: on timeout or error it allows the request through. Your site stays available even when the protection service is under load or unavailable.


Other CDNs

The same check-request API works from any edge that can send HTTP requests: use your CDN’s way to get the real client IP (e.g. edge-specific headers or the leftmost X-Forwarded-For entry) and run the same logic (call the API, then allow, redirect to challenge, or redirect to blocked). Examples: AWS Lambda@Edge, Fastly Compute, Vercel Edge, etc.

A ready-made Cloudflare or multi-CDN plugin may be offered in the future; for now, use the API directly as above.