CSP for Cookiebot: Copy-Paste Policies That Work
Table of Contents
Cookiebot is one of those tools that looks simple until CSP gets involved. Then you end up chasing blocked scripts, missing consent banners, and mysterious iframe violations.
I’ve had to wire this up on production sites more than once, and the same pattern keeps showing up: people add *.cookiebot.com somewhere, reload, and assume that’s enough. It usually isn’t.
This guide is the practical version: what to allow, where to allow it, and copy-paste policies you can actually start from.
What Cookiebot usually needs in CSP
For a standard Cookiebot setup, you’ll typically need to allow:
script-srcfor Cookiebot JavaScriptstyle-srcfor Cookiebot stylesimg-srcfor banner assets and tracking pixels if usedconnect-srcfor API/fetch/XHR callsframe-srcfor embedded consent UI- sometimes a
nonceif your site uses strict CSP
A real-world CSP header from headertest.com includes this Cookiebot-related setup:
content-security-policy:
default-src 'self' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com;
script-src 'self' 'nonce-MDVlNTdhMWEtYjQwZi00NTZiLThkMDgtZWIzMDQ4NGU1NjYy' 'strict-dynamic' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com;
style-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://*.cookiebot.com https://consent.cookiebot.com;
img-src 'self' data: https:;
font-src 'self';
connect-src 'self' https://api.headertest.com https://tallycdn.com https://or.headertest.com wss://or.headertest.com https://*.google-analytics.com https://*.googletagmanager.com https://*.cookiebot.com;
frame-src 'self' https://consentcdn.cookiebot.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
object-src 'none'
That’s a good reference because it reflects a working deployment, not a theoretical policy.
Minimum Cookiebot CSP
If you just want the smallest reasonable starting point for Cookiebot, use this:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://consent.cookiebot.com https://consentcdn.cookiebot.com;
style-src 'self' 'unsafe-inline' https://consent.cookiebot.com;
img-src 'self' data: https:;
connect-src 'self' https://consent.cookiebot.com https://consentcdn.cookiebot.com https://*.cookiebot.com;
frame-src https://consentcdn.cookiebot.com;
base-uri 'self';
form-action 'self';
object-src 'none';
That said, I usually prefer being explicit with the actual hosts you observe in production instead of spraying *.cookiebot.com everywhere.
Safer production-ready Cookiebot CSP
This is a stronger baseline for a site that already uses a reasonably strict CSP:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://consent.cookiebot.com https://consentcdn.cookiebot.com;
style-src 'self' 'unsafe-inline' https://consent.cookiebot.com;
img-src 'self' data: https:;
connect-src 'self' https://consent.cookiebot.com https://consentcdn.cookiebot.com;
frame-src 'self' https://consentcdn.cookiebot.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
object-src 'none';
Why unsafe-inline in style-src? Because consent tools often inject inline styles for UI elements. If you can replace that with style nonces or hashes, do it. In practice, many teams keep unsafe-inline for styles and stay strict on scripts.
If you use nonces with Cookiebot
A lot of modern apps run a nonce-based CSP. That’s fine with Cookiebot, but you need to be consistent.
Example header:
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-{{NONCE}}' 'strict-dynamic' https://consent.cookiebot.com https://consentcdn.cookiebot.com;
style-src 'self' 'unsafe-inline' https://consent.cookiebot.com;
img-src 'self' data: https:;
connect-src 'self' https://consent.cookiebot.com https://consentcdn.cookiebot.com https://*.cookiebot.com;
frame-src 'self' https://consentcdn.cookiebot.com;
base-uri 'self';
form-action 'self';
object-src 'none';
Then your Cookiebot script tag needs the same nonce:
<script
nonce="{{NONCE}}"
id="Cookiebot"
src="https://consent.cookiebot.com/uc.js"
data-cbid="YOUR-CBID"
data-blockingmode="auto"
type="text/javascript">
</script>
If your app generates a per-request nonce, don’t hardcode it into templates at build time. I’ve seen that mistake more than once.
Copy-paste HTML for Cookiebot
This is the standard embed most teams use:
<script
id="Cookiebot"
src="https://consent.cookiebot.com/uc.js"
data-cbid="YOUR-CBID"
data-blockingmode="auto"
type="text/javascript">
</script>
If you’re on a strict nonce-based policy:
<script
nonce="{{NONCE}}"
id="Cookiebot"
src="https://consent.cookiebot.com/uc.js"
data-cbid="YOUR-CBID"
data-blockingmode="auto"
type="text/javascript">
</script>
Nginx example
For Nginx, a straightforward header looks like this:
add_header Content-Security-Policy "
default-src 'self';
script-src 'self' https://consent.cookiebot.com https://consentcdn.cookiebot.com;
style-src 'self' 'unsafe-inline' https://consent.cookiebot.com;
img-src 'self' data: https:;
connect-src 'self' https://consent.cookiebot.com https://consentcdn.cookiebot.com https://*.cookiebot.com;
frame-src 'self' https://consentcdn.cookiebot.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
object-src 'none';
" always;
If you want to roll this out safely, start with report-only first.
add_header Content-Security-Policy-Report-Only "
default-src 'self';
script-src 'self' https://consent.cookiebot.com https://consentcdn.cookiebot.com;
style-src 'self' 'unsafe-inline' https://consent.cookiebot.com;
img-src 'self' data: https:;
connect-src 'self' https://consent.cookiebot.com https://consentcdn.cookiebot.com https://*.cookiebot.com;
frame-src 'self' https://consentcdn.cookiebot.com;
base-uri 'self';
form-action 'self';
object-src 'none';
" always;
Apache example
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' https://consent.cookiebot.com https://consentcdn.cookiebot.com; style-src 'self' 'unsafe-inline' https://consent.cookiebot.com; img-src 'self' data: https:; connect-src 'self' https://consent.cookiebot.com https://consentcdn.cookiebot.com https://*.cookiebot.com; frame-src 'self' https://consentcdn.cookiebot.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; object-src 'none';"
Express / Node.js example
If you manage CSP in app code, here’s a plain Express example:
app.use((req, res, next) => {
res.setHeader(
"Content-Security-Policy",
[
"default-src 'self'",
"script-src 'self' https://consent.cookiebot.com https://consentcdn.cookiebot.com",
"style-src 'self' 'unsafe-inline' https://consent.cookiebot.com",
"img-src 'self' data: https:",
"connect-src 'self' https://consent.cookiebot.com https://consentcdn.cookiebot.com https://*.cookiebot.com",
"frame-src 'self' https://consentcdn.cookiebot.com",
"frame-ancestors 'none'",
"base-uri 'self'",
"form-action 'self'",
"object-src 'none'"
].join("; ")
);
next();
});
And with a per-request nonce:
import crypto from "crypto";
app.use((req, res, next) => {
const nonce = crypto.randomBytes(16).toString("base64");
res.locals.nonce = nonce;
res.setHeader(
"Content-Security-Policy",
[
"default-src 'self'",
`script-src 'self' 'nonce-${nonce}' 'strict-dynamic' https://consent.cookiebot.com https://consentcdn.cookiebot.com`,
"style-src 'self' 'unsafe-inline' https://consent.cookiebot.com",
"img-src 'self' data: https:",
"connect-src 'self' https://consent.cookiebot.com https://consentcdn.cookiebot.com https://*.cookiebot.com",
"frame-src 'self' https://consentcdn.cookiebot.com",
"base-uri 'self'",
"form-action 'self'",
"object-src 'none'"
].join("; ")
);
next();
});
Template:
<script
nonce="{{nonce}}"
id="Cookiebot"
src="https://consent.cookiebot.com/uc.js"
data-cbid="YOUR-CBID">
</script>
Common CSP errors with Cookiebot
1. Refused to load the script
You’re missing Cookiebot in script-src.
Fix:
script-src 'self' https://consent.cookiebot.com https://consentcdn.cookiebot.com;
If you use nonces, also make sure the script tag carries the correct nonce.
2. Refused to connect
Cookiebot is trying to make a network request not covered by connect-src.
Fix:
connect-src 'self' https://consent.cookiebot.com https://consentcdn.cookiebot.com https://*.cookiebot.com;
If you want to tighten that later, inspect actual violations first.
3. Refused to frame
This usually means the consent UI is loaded from a Cookiebot frame host you didn’t allow.
Fix:
frame-src 'self' https://consentcdn.cookiebot.com;
4. Banner renders broken or unstyled
That’s often style-src.
Fix:
style-src 'self' 'unsafe-inline' https://consent.cookiebot.com;
I don’t love unsafe-inline, but breaking the consent UI is worse than being slightly less pure on style policy.
Cookiebot with Google Tag Manager
A very common setup is Cookiebot + GTM + GA. If that’s your stack, the headertest.com policy is a realistic reference.
A compact version:
Content-Security-Policy:
default-src 'self' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com;
script-src 'self' 'nonce-{{NONCE}}' 'strict-dynamic' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com;
style-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://*.cookiebot.com https://consent.cookiebot.com;
img-src 'self' data: https:;
connect-src 'self' https://*.google-analytics.com https://*.googletagmanager.com https://*.cookiebot.com;
frame-src 'self' https://consentcdn.cookiebot.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
object-src 'none';
That matches how these systems are commonly deployed together.
My recommendation
Start with a report-only policy, load your site through the full consent flow, and watch what actually breaks. Cookie consent tools are one of those integrations where “minimal CSP” and “working CSP” are not always the same thing.
If you need more ready-to-use CSP patterns, csp-examples.com is useful for quick policy references. For the source behavior and current embed requirements, check the official Cookiebot documentation.
The short version I’d use first on a production site is this:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://consent.cookiebot.com https://consentcdn.cookiebot.com;
style-src 'self' 'unsafe-inline' https://consent.cookiebot.com;
img-src 'self' data: https:;
connect-src 'self' https://consent.cookiebot.com https://consentcdn.cookiebot.com https://*.cookiebot.com;
frame-src 'self' https://consentcdn.cookiebot.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
object-src 'none';
Then tighten hosts based on actual traffic instead of guessing. That’s the approach that tends to survive real production deployments.