🔒 Captcha.eu CSP Integration

Production-ready Content Security Policy configuration for secure CAPTCHA integration

📋 Required CSP Headers

Content-Security-Policy:
script-src 'self' https://www.captcha.eu 'unsafe-inline';
style-src 'self' https://www.captcha.eu 'unsafe-inline';
worker-src blob:;
connect-src https://www.captcha.eu;
frame-src 'self';
img-src 'self' https://www.captcha.eu data:;
script-src Load SDK and execute initialization
style-src Widget styling and animations
worker-src blob: Background challenge processing (blob required for Firefox)
connect-src API calls for validation
frame-src V2 widget iframe rendering
img-src Logo and challenge images

🛡️ Security Benefits

Domain Scoped All permissions limited to https://www.captcha.eu only
No Third-Party Scripts External scripts blocked except CAPTCHA
Network Isolation API calls restricted to CAPTCHA domain
Worker Protection Blob workers from fetched trusted source (Firefox-compatible)
Live Widget

Working CAPTCHA

This widget is running with the recommended CSP configuration. Try solving the CAPTCHA to see it in action.

Documentation

Full CSP Guide

Complete integration guide with examples for Nginx, Apache, Node.js, Django, WordPress, and more frameworks.

Read Full Docs →

💡 Why 'unsafe-inline' is Acceptable

While 'unsafe-inline' for styles is required, it represents minimal security risk in this context:

Scripts are still restricted to your domain and https://www.captcha.eu
Network requests are limited to https://www.captcha.eu only
Workers use blob: URLs (see below)
Inline styles cannot execute code or leak data

This configuration provides strong XSS protection while allowing the widget to function properly.

⚠️ Why worker-src blob: is Required

Firefox does not support cross-origin Web Workers, even with proper CORS headers. This is per the HTML specification - Web Workers must be same-origin.

Browser behavior:
Chrome: new Worker("https://captcha.eu/worker.js") ✅ Works with CORS
Firefox: new Worker("https://captcha.eu/worker.js") ❌ SecurityError (always)

Our solution (blob pattern):
1. fetch() the worker script (respects CORS headers ✅)
2. Create a Blob from the script content
3. Create blob URL via URL.createObjectURL()
4. new Worker(blobURL) works because blob URLs are same-origin

This is the industry-standard workaround used by Monaco Editor, Ace Editor, and other major projects. The blob: directive is safe because the script content is still fetched from https://www.captcha.eu with CORS verification.