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-src for Cookiebot JavaScript
  • style-src for Cookiebot styles
  • img-src for banner assets and tracking pixels if used
  • connect-src for API/fetch/XHR calls
  • frame-src for embedded consent UI
  • sometimes a nonce if 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.