Operations
Behaviour on outage, key rotation, server examples, domains and content security policy.
Error handling & fail-open
If the CaptchaCore service is unreachable, the behavior can be configured:
Fail-open (default)
Forms are still let through. Availability > security during an outage. Recommended for most sites.
CAPTCHACORE_FAIL_OPEN=true
Fail-closed
Forms are blocked if the service does not respond. Only for highly critical systems.
CAPTCHACORE_FAIL_OPEN=false
All API calls have a 3-second timeout. On timeout, your fail-open/closed configuration takes over.
If the visitor's browser still cannot reach the service after three retries, the widget sends the placeholder cc-unavailable instead of an empty field. Your integration then asks the service as usual: if it is down, its fail-open rule applies, otherwise the request is rejected. With data-fail-open="false" on the script tag, the field stays empty and the form waits.
Keys & rotation
Every site has a key pair: a site key (public, in the browser) and a secret key (secret, server only).
Key formats
cc_pub_6e65b5c4597a688c93f3be6e345ee1a5... ← 7 + 64 hex = 71 Zeichen cc_sec_8bb043aa6424a1fdb3e048d2d3232894... ← 7 + 64 hex = 71 Zeichen
Key rotation
On rotation, the old key is moved to rotated status and remains valid for another 24 hours. This enables rolling deployments with zero downtime.
Secret key security
- The secret key is stored in the database as a SHA-256 hash
- The plain text is shown only once after creation
- Cannot be recovered — if lost, generate a new key
- Never output in logs, API responses or error messages
Backend examples
The verify endpoint can be called from any backend. Here are examples for common languages:
PHP (no framework)
$token = $_POST['captchacore_token'] ?? ''; $ch = curl_init('https://api.captchacore.eu/api/v2/verify'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 3, CURLOPT_HTTPHEADER => [ 'X-CaptchaCore-Key: cc_sec_IHR_SECRET', 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => json_encode([ 'token' => $token, 'form_type' => 'contact', ]), ]); $result = json_decode(curl_exec($ch), true); if (!$result['valid']) { die('Bot erkannt.'); }
Node.js
const token = req.body.captchacore_token; const res = await fetch('https://api.captchacore.eu/api/v2/verify', { method: 'POST', headers: { 'X-CaptchaCore-Key': process.env.CAPTCHACORE_SECRET_KEY, 'Content-Type': 'application/json', }, body: JSON.stringify({ token, form_type: 'register' }), }); const { valid, risk_score, action } = await res.json(); if (!valid) return res.status(403).json({ error: 'Bot detected' });
Python
import requests result = requests.post( 'https://api.captchacore.eu/api/v2/verify', headers={'X-CaptchaCore-Key': 'cc_sec_IHR_SECRET'}, json={'token': token, 'form_type': 'login'}, timeout=3, ).json() if not result['valid']: raise Exception('CaptchaCore verification failed')
Infrastructure & domains
Domain architecture
| Domain | Purpose | Type |
|---|---|---|
| api.captchacore.eu | Challenge, verify, status, version | API (JSON) |
| src-eu.captchacore.eu | Widget JS + Worker (EU-only CDN) | CDN (default) |
| src.captchacore.eu | Widget JS + Worker (global CDN) | CDN (optional) |
| captchacore.eu | Website, admin panel, docs | Frontend |
Content Security Policy (CSP)
If you use a Content Security Policy on your website, the following domains must be allowed:
# Minimal (EU-only CDN + API) script-src https://src-eu.captchacore.eu; connect-src https://api.captchacore.eu https://src-eu.captchacore.eu; worker-src blob:; # Mit globalem CDN (zusätzlich) script-src https://src.captchacore.eu; connect-src https://src.captchacore.eu;
Why these entries?
- script-src — The widget JS (
captchacore-v2.min.js) is loaded from the CDN - connect-src (API) — The widget calls
/api/v2/challengevia fetch() - connect-src (CDN) — The PoW worker is loaded from the CDN via fetch()
- worker-src blob: — The worker is instantiated as a blob URL (avoids CORS)
Strict CSP with a nonce
The widget brings its look as a <style> element. If your CSP does not allow 'unsafe-inline' for styles, give the script tag your page nonce — the widget applies it to its styles automatically:
<script nonce="IHR_NONCE" src="https://src-eu.captchacore.eu/widget/captchacore-v2.min.js" data-service-url="https://api.captchacore.eu" data-site-key="cc_pub_…" async defer></script> # passende Richtlinie script-src 'nonce-IHR_NONCE' https://src-eu.captchacore.eu; style-src-elem 'self' 'nonce-IHR_NONCE'; connect-src 'self' https://api.captchacore.eu https://src-eu.captchacore.eu; worker-src 'self' blob:;
The widget uses no inline event handlers and no eval() — 'unsafe-inline' and 'unsafe-eval' are never needed for scripts.
The Symfony bundle and the Laravel package set the nonce via the nonce option; without it, the Laravel package uses the Vite nonce. The nonce must be allowed in script-src and style-src-elem.
CORS headers
CaptchaCore automatically sets the correct CORS headers on all API endpoints. You do not need any CORS configuration on your side. The API responds with:
Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, POST, OPTIONS Access-Control-Allow-Headers: Content-Type, Accept, X-CaptchaCore-Key
NGINX example for your website
If you have configured a strict CSP on your website:
# NGINX — CSP für CaptchaCore Widget add_header Content-Security-Policy "script-src 'self' https://src-eu.captchacore.eu; connect-src 'self' https://api.captchacore.eu https://src-eu.captchacore.eu; worker-src 'self' blob:;"