Security & CSP
What the browser script can and can't do, the Content-Security-Policy directives it needs, and why the proxy install is the strongest setup.
A script in your <head> runs with full page privileges, so it's fair to ask what ours actually does before you ship it. This page is the honest answer, plus the exact CSP directives to lock it down.
What the script can and can't do
The SDK is a single minified IIFE. You can read the whole thing — it's served unobfuscated at https://cdn.datalook.app/s.js.
| Property | Status |
|---|---|
eval, Function(), innerHTML | None. Nothing dynamic to inject into. |
Cookies / localStorage | Not used. The visitor id is hashed server-side; the browser stores no identifier. |
| Active fingerprinting (canvas, WebGL, fonts) | None. |
| Credentials on requests | We never read or set cookies. The fetch fallback adds credentials: 'omit'; on a same-origin (proxied) install sendBeacon may carry your site's own cookies, which the collector ignores. |
| Global footprint | One global, window.analytics. No prototype patching. |
| Failure isolation | Sends are wrapped in try/catch; a bot context gets no-op stubs. A failure can't throw into your render. |
| PII in custom properties | Keys matching email / phone / password / ssn are dropped in the browser and again server-side. |
One global we do touch
To track route changes in single-page apps, the SDK wraps history.pushState and replaceState (it calls your original and preserves its return value). This is standard for SPA analytics. It's the only host global we touch.
What we collect from the browser
Beyond the URL, referrer, and UTM tags, each event carries a small env object: screen and viewport size, device pixel ratio, color depth, language, timezone (name and UTC offset), and — when the browser exposes it — a coarse network type (e.g. "4g"). This is passive context, not active fingerprinting — but it's not nothing, so we name it here rather than hide it. No PII, no canvas hashes, no font enumeration.
Content-Security-Policy
The SDK needs no unsafe-inline and no unsafe-eval — it's an external script that only loads and sends data. If you run a CSP, allow our origin for loading (script-src) and sending (connect-src):
script-src 'self' https://cdn.datalook.app;
connect-src 'self' https://cdn.datalook.app;connect-src covers both navigator.sendBeacon and the fetch fallback.
Tightening CSP with the proxy
How much the first-party proxy setup tightens your CSP depends on which path you take.
Framework path — script and collector both load from your own origin, so the directives collapse to nothing external:
script-src 'self';
connect-src 'self';No external origin to allowlist, nothing for an ad blocker to match. This is the strongest setup on every axis.
DNS path — your events post first-party, but s.js still loads from our CDN, so script-src keeps our origin while connect-src gains your subdomain:
script-src 'self' https://cdn.datalook.app;
connect-src 'self' https://metrics.you.com https://cdn.datalook.app;cdn.datalook.app stays in connect-src because the collector falls back to it until metrics.you.com resolves.
Why there's no SRI hash
Subresource Integrity pins a script to a fixed hash. We ship fixes and improvements to s.js on the CDN, so a pinned hash would break the moment we update — the same constraint every auto-updating analytics script lives with. Instead, the script is small enough to read end to end, and the framework-path proxy install lets you serve it from infrastructure you control.
Transport details
navigator.sendBeacon(<endpoint>, body)with afetch({ keepalive: true })fallback.Content-Type: text/plainso the request is a CORS simple request — no preflight to misconfigure.- The endpoint is resolved relative to the script's URL — or, on the DNS path, your
data-domainsubdomain once it resolves (see the proxy overview).
What's next
- Shrink (DNS path) or eliminate (framework path) the third-party origin with the first-party proxy.
- The full privacy stance (cookieless ids, IP handling) lives on the marketing site's privacy page.