Content Security Policy
If your site sends a Content-Security-Policy header, allow Specto's host for the script and its uploads. Nothing else is needed to record.
Directives
Content-Security-Policy: script-src 'self' https://specto.sh; # the snippet (not needed with the npm package) connect-src 'self' https://specto.sh; # uploads, sendBeacon and the project's settings worker-src 'self' blob:; # the upload worker (optional, see below)
| Directive | Why |
|---|---|
script-src | Loads https://specto.sh/specto.js (or a pinned /sdk/<version>/specto.js). The npm package is bundled into your own scripts. |
connect-src | Batches are POSTed to /api/ingest (also via sendBeacon when the page is hidden); the project's settings come from /api/sdk/config; the feedback widget posts to /api/feedback. |
worker-src | Uploads run in a Web Worker created from a blob: URL. If your policy blocks it, Specto falls back to uploading from the page itself — it still works, just with the work on the main thread. |
The feedback widget
With data-feedback="true" the widget draws its button and form in a shadow root styled by an inline <style>, so it needs style-src 'unsafe-inline'. It builds that markup with innerHTML, so leave it off where you enforce Trusted Types. Recording itself needs neither.
Nonce-based policies
With 'strict-dynamic' or nonces, host allowlists in script-src are ignored: give the snippet your page's nonce.
<script nonce="{your nonce}" src="https://specto.sh/specto.js" data-key="pk_your_key" defer></script>
Pinning with Subresource Integrity
Pinned versions never change, so you can lock them with integrity — the evergreen /specto.js updates and must not be pinned. The exact tag for the latest release is in the overview and in Settings → Install.
Self-hosted
Use your instance's origin in the directives. If ingest runs on its own hostname, that one goes in connect-src.

