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.atmosvere.eu (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.atmosvere.eu';
const CHECK_REQUEST_TIMEOUT_MS = 400; // fail-open if the API is slower than this
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 (empty on first visit until the API sets the cookie)
const cookieHeader = request.headers.get('cookie') ?? '';
const trustedId = getTrustedIdFromCookie(cookieHeader) ?? '';
// Path the visitor requested, including query string
const requestedPath = url.pathname + url.search;
// (optional) Request headers for stronger detection — exclude authorization, cookie, etc.
const headers = {
'user-agent': request.headers.get('user-agent') ?? '',
referer: request.headers.get('referer') ?? '',
};
// (optional) Cookie names only, never values (e.g. ['trusted_id'])
const cookies = getCookieNamesFromHeader(cookieHeader);
// 3) Send these fields to Atmosvere in the POST body
const body = { ip, requestedPath, trustedId };
// (optional) include headers and cookies when available
if (Object.keys(headers).length) body.headers = headers;
if (cookies.length) body.cookies = cookies;
// Default to allow if the API is slow or unreachable (fail-open)
let checkResponse = { decision: 'allow' };
try {
// 4) Fail-safe timeout: abort slow API calls so you still serve the page (fail-open)
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
// Forward Set-Cookie so the visitor receives trusted_id from the API
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://pages.atmosvere.eu/blocked.html?${params}`
: `https://pages.atmosvere.eu/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 '';
}
}
// Extract cookie names only (never values) from the Cookie header
function getCookieNamesFromHeader(cookieHeader) {
if (!cookieHeader) return [];
return cookieHeader
.split(';')
.map((part) => part.trim().split('=')[0])
.filter(Boolean);
}
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
The Worker example above sends optional headers (e.g. user-agent, referer) and cookies (cookie names only, never values) when available. Do not send sensitive header values such as authorization or the raw cookie header.
trustedId: Parse thetrusted_idcookie value from theCookieheader and send it in the body. Send cookie names separately viacookiesfor stronger detection.- Set-Cookie: If the API response includes a
Set-Cookieheader, forward it to the client (e.g. copy it onto the response you return) so the visitor gets thetrusted_idcookie.
For the full field list and rules, see Server-side Integration.
Challenge and block
On challenge or block, redirect the user to our hosted pages with the same query params as in Step 2: Activate active protection:
trusted_id— from the visitor’s cookie or the API response’ssessionId.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).
| Page | URL |
|---|---|
| Challenge | https://pages.atmosvere.eu/challenge.html |
| Blocked | https://pages.atmosvere.eu/blocked.html |
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.