Content Security Policy (CSP)
If your site enforces a Content Security Policy, you must allowlist certain Opportify origins for the Fraud Protection script to operate correctly. This page documents every required directive, explains why each is needed, and provides ready-to-use policy snippets.
The Opportify script does not require 'unsafe-inline' or 'unsafe-eval' in any directive. It is loaded as an external script, communicates exclusively via fetch() with CORS, and does not inject inline scripts or evaluate runtime code.
Required Directives
Add the following origins to your existing CSP header or <meta> tag:
Content-Security-Policy:
script-src 'self' https://cdn.opportify.ai;
connect-src 'self' https://api.opportify.ai https://form.opportify.ai;
frame-src 'self' https://cdn.opportify.ai;
form-action 'self' https://form.opportify.ai;
These origins should be appended to your current directives — do not replace your existing script-src or other values.
Directive Breakdown
script-src
| Origin | Purpose |
|---|---|
https://cdn.opportify.ai | Loads the versioned Fraud Protection script (/f/v{version}.min.js) |
The script is served from Opportify's CDN over HTTPS with immutable cache headers. No inline scripts are injected at any point.
connect-src
| Origin | Purpose |
|---|---|
https://api.opportify.ai | Session initialization, telemetry event ingestion, and token minting |
https://form.opportify.ai | Proxied form submissions via POST /intel/v1/submit/{endpointId} |
All requests use the fetch() API with Content-Type: application/json and CORS mode. No WebSocket, EventSource, or Beacon connections are made.
frame-src
| Origin | Purpose |
|---|---|
https://cdn.opportify.ai | A hidden zero-pixel <iframe> used for secure cross-origin session coordination |
The iframe is invisible (zero width/height, no border) and is never displayed to the user. It uses postMessage to communicate with the parent page and is origin-validated on both sides.
If this directive is missing, the script will emit an ERR_IFRAME_TIMEOUT error after 1 second and fire the opportify:init-failed event.
form-action
| Origin | Purpose |
|---|---|
https://form.opportify.ai | Allows native form POST submissions to your Opportify endpoint |
This directive is needed when the form's action attribute points directly to https://form.opportify.ai/intel/v1/submit/{endpointId}. If you use submit interception mode (the default), the browser performs a fetch() rather than a native form POST — but you should still include this directive as a fallback guarantee.
Full Policy Example
A complete CSP header that includes all Opportify directives alongside typical application defaults:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.opportify.ai;
connect-src 'self' https://api.opportify.ai https://form.opportify.ai;
frame-src 'self' https://cdn.opportify.ai;
form-action 'self' https://form.opportify.ai;
style-src 'self';
img-src 'self' data:;
font-src 'self';
Meta Tag Alternative
If you set CSP via an HTML <meta> tag instead of a response header:
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self' https://cdn.opportify.ai; connect-src 'self' https://api.opportify.ai https://form.opportify.ai; frame-src 'self' https://cdn.opportify.ai; form-action 'self' https://form.opportify.ai;"
/>
The <meta> approach does not support all directives (notably frame-ancestors and report-uri). Prefer server-side response headers for full CSP control.
Framework-Specific Configuration
Next.js
Configure CSP in your next.config.js security headers:
// next.config.js
const securityHeaders = [
{
key: 'Content-Security-Policy',
value: [
"default-src 'self'",
"script-src 'self' https://cdn.opportify.ai",
"connect-src 'self' https://api.opportify.ai https://form.opportify.ai",
"frame-src 'self' https://cdn.opportify.ai",
"form-action 'self' https://form.opportify.ai",
].join('; '),
},
];
module.exports = {
async headers() {
return [{ source: '/(.*)', headers: securityHeaders }];
},
};
SvelteKit
Add the directives in svelte.config.js:
// svelte.config.js
const config = {
kit: {
csp: {
directives: {
'default-src': ['self'],
'script-src': ['self', 'https://cdn.opportify.ai'],
'connect-src': ['self', 'https://api.opportify.ai', 'https://form.opportify.ai'],
'frame-src': ['self', 'https://cdn.opportify.ai'],
'form-action': ['self', 'https://form.opportify.ai'],
},
},
},
};
export default config;
Nginx
add_header Content-Security-Policy
"default-src 'self'; script-src 'self' https://cdn.opportify.ai; connect-src 'self' https://api.opportify.ai https://form.opportify.ai; frame-src 'self' https://cdn.opportify.ai; form-action 'self' https://form.opportify.ai;"
always;
Apache
Header always set Content-Security-Policy \
"default-src 'self'; script-src 'self' https://cdn.opportify.ai; connect-src 'self' https://api.opportify.ai https://form.opportify.ai; frame-src 'self' https://cdn.opportify.ai; form-action 'self' https://form.opportify.ai;"
What is NOT Required
The Opportify script is designed to work within strict CSP policies. The following directives do not need any Opportify-specific entries:
| Directive | Reason |
|---|---|
style-src | No inline styles injected. DOM styling is done via property assignment (element.style.*), which is not restricted by CSP. |
img-src | No images loaded by the script. |
font-src | No fonts loaded. |
worker-src | No Web Workers, Service Workers, or Shared Workers created. |
object-src | No plugins, embeds, or applets used. |
media-src | No audio or video loaded. |
'unsafe-eval' | No eval(), new Function(), or string-based setTimeout/setInterval. |
'unsafe-inline' | No inline <script> tags, event handlers, or javascript: URLs. |
Browser Requirements
The Fraud Protection script requires the Web Crypto API (crypto.subtle) to be available. This API is only exposed in secure contexts (HTTPS pages). If unavailable, the script gracefully skips initialization.
Verifying Your Policy
After updating your CSP, verify the script works correctly:
- Open your page in a browser.
- Open DevTools → Console and look for CSP violation messages (they start with
Refused to...). - Open DevTools → Network and confirm:
- The script loads from
cdn.opportify.ai(status 200). - An
initrequest fires toapi.opportify.ai(status 200). - Submitting the form sends to
form.opportify.ai(status 200 or 302).
- The script loads from
- Check that no
opportify:init-failedevent fires (listen in Console).
Deploy a Content-Security-Policy-Report-Only header first to detect violations without blocking resources:
Content-Security-Policy-Report-Only:
default-src 'self';
script-src 'self' https://cdn.opportify.ai;
connect-src 'self' https://api.opportify.ai https://form.opportify.ai;
frame-src 'self' https://cdn.opportify.ai;
form-action 'self' https://form.opportify.ai;
report-uri /csp-reports;
Once your reports come back clean, promote the header to the enforcing Content-Security-Policy.
Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
Refused to load the script in console | script-src missing https://cdn.opportify.ai | Add the origin to your script-src directive |
Refused to connect to in console | connect-src missing https://api.opportify.ai or https://form.opportify.ai | Add both origins to connect-src |
Refused to frame in console | frame-src missing https://cdn.opportify.ai | Add the origin to frame-src |
opportify:init-failed event fires | Bridge iframe blocked by CSP or network | Verify frame-src includes https://cdn.opportify.ai |
| Form submits but redirects are blocked | form-action missing https://form.opportify.ai | Add the origin to form-action |
| Script loads but sessions fail silently | Page not served over HTTPS (Web Crypto unavailable) | Serve your site over HTTPS |
| CSP violations only in production | Different headers between environments | Compare response headers using DevTools → Network → select document → Headers |
Related Pages
- Installation & Setup — script loading instructions and quick-start guide
- Error Reference — full list of errors, including CSP-related failures
- Configuration Reference — all script and form attributes