Documentation
ConsentLab + CSP
01 · Why
A strict CSP, without unsafe-inline
A strict Content-Security-Policy is one of the best protections against XSS attacks: the browser only executes the scripts and styles your policy explicitly allows, via a nonce generated by your server on every request.
Many third-party tools ask you to add 'unsafe-inline' to your policy to work. Never do it: it defeats most of the protection your CSP provides, for your entire site.
ConsentLab does not need it: the widget picks up your page’s nonce at load time and applies it automatically to everything it injects, its styles and the scripts it unblocks after consent.
02 · Integration
Set the nonce on the loader
Recommended: the native nonce attribute
Set the standard nonce attribute on the loader tag, exactly like any other script on your page, with the value your server generates on each request (snippet on the right).
That’s it: the widget reads the nonce from its own tag at boot and propagates it to everything it injects. No extra attribute, no configuration.
Alternative: data-cc-nonce (dynamically injected loader)
Browsers clear the nonce attribute of elements inserted into the DOM (anti-exfiltration protection). If your loader is injected dynamically (tag manager, init script…), the widget cannot read the native nonce back: pass it via data-cc-nonce as well (snippet on the right).
data-cc-nonce is reserved for this exact case and must be set on the ConsentLab loader tag only: the widget uses the first script[data-cc-nonce] in the document. For a classic HTML integration, use the native nonce attribute.
03 · Policy
The recommended policy
Example of a strict policy compatible with ConsentLab and your other scripts (right-hand column).
- With
'strict-dynamic', scripts created by our nonced loader inherit its trust (modern browsers): scripts unblocked on consent run without allowlisting every third-party domain. - Injected styles, however, always need the nonce:
'strict-dynamic'only applies to scripts. That is why the widget propagates the nonce natively. - Without
'strict-dynamic', it works too: the widget sets the nonce everywhere it is needed (styles and executable script copies). In that case add the origins of external scripts toscript-src(next section). - The trailing
https:inscript-srcis a fallback for browsers that do not support'strict-dynamic'(browsers that do support it ignore it).
04 · Origins
What your policy must allow
connect-src https://api.consentlab.eu: the widget loads its configuration and records consent proofs there. If you point the widget to another instance viadata-cc-api, allow that origin instead.script-src https://cdn.consentlab.eu: only if the loader comes from the CDN and your policy does not use'strict-dynamic'(otherwise the nonce on the tag is enough).- The domains of the tools you unblock on consent (for example
www.googletagmanager.com,*.google-analytics.com) inscript-src/connect-src, as their docs require, unless'strict-dynamic'already covers their scripts.
05 · Troubleshooting
Two symptoms, one cause
Two typical symptoms when the nonce does not reach the widget: in both cases, the developer console (F12) shows explicit CSP violations (typical messages on the right).
Consent is accepted but has no effect
The banner works, the choice is saved, but the blocked scripts (type="text/plain" + data-cc) never run: your script-src blocks the executable copies the widget creates on unblock. Same cause, same fix: the widget can only propagate the nonce it received via its tag (nonce or data-cc-nonce).
06 · FAQ
Nonce, meta tag and edge cases
Do I need to add 'unsafe-inline' for ConsentLab?
No, never. The widget is designed for nonce-based policies: set the nonce on the loader tag and it is propagated to every injection (styles and scripts). Adding 'unsafe-inline' would weaken XSS protection for your entire site.
My site has no CSP, do I need to do anything?
No. When no nonce is detected, the widget’s CSP support is fully inactive: no attribute added, no behaviour change.
Why the nonce if I already use 'strict-dynamic'?
'strict-dynamic' only covers scripts: the styles the widget injects (banner, preferences modal, consent walls) still require a valid nonce under style-src. And if your policy does not use 'strict-dynamic', the nonce is also propagated to the scripts unblocked on consent.
I use a CSP meta tag, not a header: compatible?
Yes, the mechanism is identical: the browser enforces the policy from the <meta http-equiv> tag and the widget propagates the nonce the same way. The HTTP header remains recommended (it covers more directives and applies earlier).
Need help with your CSP?
Send us your CSP header and the console violations: our team replies within 24h.
The tag with nonce
<script src="https://cdn.consentlab.eu/widget/v1/consentlab.min.js" data-cc-key="YOUR_API_KEY" nonce="{NONCE}" defer ></script>
Dynamically injected loader
// Only if the loader is injected dynamically. const s = document.createElement('script'); s.src = 'https://cdn.consentlab.eu/widget/v1/consentlab.min.js'; s.defer = true; s.dataset.ccKey = 'YOUR_API_KEY'; s.dataset.ccNonce = nonce; // the current page's CSP nonce s.nonce = nonce; // required to execute the loader itself document.head.appendChild(s);
Recommended policy
Content-Security-Policy: script-src 'nonce-{NONCE}' 'strict-dynamic' https:; style-src 'nonce-{NONCE}'; connect-src 'self' https://api.consentlab.eu;
Violation · styles
Refused to apply inline style because it violates the following
Content Security Policy directive: "style-src 'nonce-…'".Violation · scripts
Refused to execute inline script because it violates the following
Content Security Policy directive: "script-src 'nonce-…'".