Content security policy (CSP)

If you enforce a Content Security Policy (CSP) on your website, you need to add directives that let the JavaScript client load and communicate with Customer.io. This page shows the required directives.

A Content Security Policy (CSP) is a standard that helps you control the resources that a web browser can load on your page. If you enforce a CSP on your website, you need to allow the domains that our JavaScript client communicates with.

Tip

If you’re using our legacy JavaScript snippet, where calls use the _cio prefix, see the legacy CSP page instead.

Standard directives

script-src cdp.customer.io;
connect-src cdp.customer.io *.api.gist.build *.cloud.gist.build;
frame-src renderer.gist.build code.gist.build;
style-src 'unsafe-inline';
script-src cdp-eu.customer.io;
connect-src cdp-eu.customer.io *.api.gist.build *.cloud.gist.build;
frame-src renderer.gist.build code.gist.build;
style-src 'unsafe-inline';

Use nonce-based authorization

If your CSP uses nonce-based authorization, you can add a nonce to the JavaScript client snippet instead of allowing entire domains in script-src. This provides a stricter security model because it authorizes specific script elements rather than entire domains.

Add the nonce attribute to the snippet’s <script> tag, matching the nonce value in your CSP header:

HTML
<script nonce="your-random-nonce-value">
  // Your JavaScript client snippet code here
</script>

Your CSP header should include the matching nonce:

script-src 'nonce-your-random-nonce-value' cdp.customer.io;

Note

You still need to allow cdp.customer.io in script-src because the snippet dynamically loads additional scripts from this domain.

Package-based installations

If you installed the JavaScript client as a package—for example, @customerio/cdp-analytics-browser—rather than using the snippet, you don’t need to allow cdp.customer.io in script-src. The library bundles into your app code. You still need the connect-src directives so the library can send data to Customer.io.

connect-src cdp.customer.io *.api.gist.build *.cloud.gist.build;
frame-src renderer.gist.build code.gist.build;
style-src 'unsafe-inline';

Web push

If you use web push, your pages register a service worker from your own domain. Browsers check a service worker against worker-src, or, if your CSP doesn’t set it, the first of child-src, script-src, and default-src that it does set. If that directive doesn’t allow 'self', add worker-src 'self' so the browser can register the worker:

worker-src 'self';

The snippet loads the web push plugin from cdp.customer.io (or cdp-eu.customer.io), which your script-src already allows.

The service worker reports delivered and opened metrics to track.customer.io (or track-eu.customer.io in the EU region). Your pages don’t call this host, so you don’t need to add it to your pages’ CSP. If your server sends a CSP header with the service worker file itself, add the track host to that header’s connect-src.

From version 0.8.0, the service worker updates itself by loading its latest code from cdp.customer.io (or cdp-eu.customer.io). Before a browser first registers the service worker, the plugin checks from your page that this host responds, so your pages’ connect-src must allow it. The standard directives already allow this host in connect-src, because the JavaScript client sends its data there, whether you use the snippet or the npm package. If your server sends a CSP header with the service worker file itself, also add the host to that header’s script-src:

script-src https://cdp.customer.io https://cdp-eu.customer.io;

If your CSP blocks the host, the service worker keeps working with the copy in your hosted file, but it doesn’t update itself. To avoid adding a Customer.io host, serve the code from your own domain with the runtimeUrl option, or set runtimeUrl to false.

Custom proxy setups

If you’ve set up a custom proxy to route requests through your own domain, replace the Customer.io domains in your CSP with your proxy domain. For example, if your proxy domain is analytics.example.com:

script-src analytics.example.com;
connect-src analytics.example.com *.api.gist.build *.cloud.gist.build;

Glossary of directives

Some of these directives are required for in-app messaging, which includes features like inbox messages, web-based microsurveys, and so on. If you don’t use these features, you can exclude the relevant directives.

DirectiveHostDescription
script-srccdp.customer.ioThe host for the JavaScript client. The snippet loads the library and integration bundles from this domain.
connect-srccdp.customer.ioRequired for sending tracking data—identify, track, and page calls—and loading integration settings.
connect-src*.api.gist.build, *.cloud.gist.buildRequired for in-app messaging. We use these domains for message queues and real-time updates. You can exclude these if you don’t send in-app messages.
frame-srcrenderer.gist.build, code.gist.buildRequired for rendering in-app messages in iframes. You can exclude these if you don’t send in-app messages.
style-src'unsafe-inline'Required because the in-app messaging infrastructure injects inline <style> blocks to position and display messages. You can exclude this if you don’t send in-app messages.

Frequently asked questions

Do you need unsafe-inline or unsafe-eval in script-src?

No! Unlike our legacy JavaScript snippet, the JavaScript client loads as an external script. You don’t need unsafe-inline or unsafe-eval in your script-src directive. This is one of the benefits of upgrading from the legacy snippet.

Can you exclude in-app messaging directives?

Yes. If you don’t send in-app messages (including inbox messages, microsurveys, and so on), you can remove all gist domains from your CSP:

  • *.api.gist.build and *.cloud.gist.build from connect-src
  • renderer.gist.build and code.gist.build from frame-src
  • 'unsafe-inline' from style-src
Updated October 26, 2026