Skip to main content

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.

No unsafe directives required

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;
Merge with your existing policy

These origins should be appended to your current directives — do not replace your existing script-src or other values.


Directive Breakdown

script-src

OriginPurpose
https://cdn.opportify.aiLoads 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

OriginPurpose
https://api.opportify.aiSession initialization, telemetry event ingestion, and token minting
https://form.opportify.aiProxied 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

OriginPurpose
https://cdn.opportify.aiA 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

OriginPurpose
https://form.opportify.aiAllows 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;"
/>
Meta tag limitations

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:

DirectiveReason
style-srcNo inline styles injected. DOM styling is done via property assignment (element.style.*), which is not restricted by CSP.
img-srcNo images loaded by the script.
font-srcNo fonts loaded.
worker-srcNo Web Workers, Service Workers, or Shared Workers created.
object-srcNo plugins, embeds, or applets used.
media-srcNo 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:

  1. Open your page in a browser.
  2. Open DevTools → Console and look for CSP violation messages (they start with Refused to...).
  3. Open DevTools → Network and confirm:
    • The script loads from cdn.opportify.ai (status 200).
    • An init request fires to api.opportify.ai (status 200).
    • Submitting the form sends to form.opportify.ai (status 200 or 302).
  4. Check that no opportify:init-failed event fires (listen in Console).
Use report-only to test safely

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

SymptomLikely CauseFix
Refused to load the script in consolescript-src missing https://cdn.opportify.aiAdd the origin to your script-src directive
Refused to connect to in consoleconnect-src missing https://api.opportify.ai or https://form.opportify.aiAdd both origins to connect-src
Refused to frame in consoleframe-src missing https://cdn.opportify.aiAdd the origin to frame-src
opportify:init-failed event firesBridge iframe blocked by CSP or networkVerify frame-src includes https://cdn.opportify.ai
Form submits but redirects are blockedform-action missing https://form.opportify.aiAdd the origin to form-action
Script loads but sessions fail silentlyPage not served over HTTPS (Web Crypto unavailable)Serve your site over HTTPS
CSP violations only in productionDifferent headers between environmentsCompare response headers using DevTools → Network → select document → Headers