Published Updated

Cookiebot guide

A step-by-step guide to setting up Cookiebot and configuring and testing consent states for Google Consent Mode, Microsoft UET and Clarity.

Cartoon turtle with glasses standing beside a browser window showing the Cookiebot logo and a cookie shield
Generated with OpenAI ImageGen

Ask AI to summarize:

This guide takes a direct Cookiebot website implementation from adding the scripts to browser validation. Site owners can use the implementation notes, marketing and analytics specialists can use the expected consent states, and developers can copy and adapt the generated code.

Before you start

This guide covers direct website implementation: you add the consent defaults and Cookiebot tag directly to the website’s <head>. You need a Cookiebot Domain Group ID and access to the website source or a CMS feature that can insert scripts into <head>.

Do not combine these scripts with a Cookiebot CMP template in Google Tag Manager or another integration that already controls consent defaults and updates. Audit or remove the existing implementation before continuing, because two integrations controlling the same consent state can conflict.

This is an implementation guide, not a legal determination. The categories, regional behavior, retention period and banner wording still need to match the site’s policy and applicable requirements.

Both snippets below always set the defaults for Google Consent Mode v2. If the website uses Microsoft Advertising or Clarity, the same snippets can also set Microsoft UET and Microsoft Clarity Consent API v2 defaults. Microsoft Advertising enforces consent signals in the EEA, the UK and Switzerland, and UET assumes granted when no default is set, so an included UET integration needs the denied default.

Basic and Advanced Consent Mode describe when a tag loads, not which default snippet you copy. With Basic Consent Mode, the tag stays blocked until the relevant consent is granted. With Advanced Consent Mode, the tag loads with denied defaults and sends limited cookieless measurements. This guide uses Basic mode for Microsoft UET. Your Google tags can use either mode, depending on their loading rules. See Google’s Basic versus Advanced comparison and the Cookiebot UET article.

UET reads ad_storage only. Microsoft Clarity is a separate product with its own Consent API v2 and reads both ad_Storage (marketing) and analytics_Storage (statistics). When selected, the snippets initialize these values before the relevant Microsoft tag loads. Since 31 October 2025 both UET and Clarity enforce consent signals for EEA, UK and Swiss traffic.

Step 2 - Add Cookiebot to the website

Place the generated Cookiebot tag as the first script inside <head>. This is especially important with automatic blocking, because anything that loads before Cookiebot can escape its control.

  • Automatic blocking uses data-blockingmode="auto" and must not use async or defer.
  • Manual blocking loads Cookiebot with async; you must then mark every non-essential script, iframe and image with the correct consent category. See Cookiebot’s automatic and manual blocking guides.

If a WordPress plugin outputs Google Tag Manager, verify the final page source rather than the plugin settings alone. In this implementation, Cookiebot still needs to run before Google Tag Manager and any other tag that could set non-essential cookies.

Customize the Cookiebot script
Copy the Domain Group ID from your Cookiebot Manager.
Advanced: disable an automatic consent integration

The generated tag keeps Cookiebot’s Google, Microsoft UET and Microsoft Clarity consent integrations enabled. Add a disable attribute manually only when your own implementation supplies that service’s complete consent update lifecycle, including granted and denied states, later changes, consent withdrawal and returning visitors.

Integration replaced by your own code Attribute to add to the Cookiebot tag
Google Consent Mode data-consentmode="disabled"
Microsoft UET Consent Mode data-ms-consent-mode="disabled"
Microsoft Clarity Consent Mode data-ms-clarity-consent-mode="disabled"

Disable only the integration you replace. If a service is not used at all, omit its tag and related code instead of disabling Cookiebot’s consent passing.

Choose a fixed language when each language version of the website should always show its matching banner. Leave the field on auto-detect when Cookiebot should use the visitor’s browser language.

View all data-culture language codes
Language Code in ‘data-culture’
Arabic AR
Bulgarian BG
Catalan CA
Czech CS
Welsh CY
Danish DA
Deutsch DE
Greek (modern) EL
English EN
Spanish ES
Estonian ET
Basque EU
Finnish FI
French FR
Irish GA
Hebrew HE
Hindi HI
Croatian HR
Hungarian HU
Indonesian ID
Icelandic IS
Italian IT
Japanese JA
Korean KO
Lithuanian LT
Latvian LV
Macedonian MK
Malay MS
Norwegian Bokmål NB
Dutch NL
Polish PL
Portuguese PT
Brazilian Portuguese PT-BR
Romanian RO
Russian RU
Sinhalese SI
Slovak SK
Slovenian SL
Albanian SQ
Serbian SR
Swedish SV
Tamil TA
Thai TH
Turkish TR
Ukrainian UK
Vietnamese VI
Chinese ZH
Traditional Chinese ZH-HANT

The Cookie Declaration is optional. Add it where the site’s discovered cookies and trackers should be listed, such as a cookie policy page. It uses the Domain Group ID entered in Step 2. You can select its language independently.

Customize the Cookie Declaration script
This field is filled automatically with the Cookiebot ID you entered in Step 2.

Cookiebot’s Privacy Trigger already lets visitors change or withdraw consent. If the site needs another entry point, add a button to the footer or privacy page. It works after the Cookiebot script has loaded.


<button type="button" onclick="Cookiebot.renew()">
  Cookie settings
</button>

Use Google Tag Assistant as the primary Google check: confirm the earliest Consent event contains the default and the latest Consent event contains the update. The Console helper below is a quick supplementary check for Google, UET and Clarity on the current page.

Before consent, check the following:

  • Every Google signal except security_storage should be denied.
  • UET should have an ad_storage default of denied or remain blocked in Basic mode.
  • Clarity should report both storage values as DENIED.

Run the helper again after accepting and after withdrawing consent to confirm that the consent states update in both cases.

(() => {

  // Defined inside the function so the helper can be pasted repeatedly.
  const consentStatusString = status =>
    status === undefined ? "" : status ? "granted" : "denied";

  const consentStatusColor = status =>
    status === "granted" ? "color: #4AF626" : "color: #ef2929";

  /* =========================================================
     GOOGLE CONSENT MODE
     ========================================================= */

  console.log(
    "%cGoogle Consent Mode",
    "font-weight: bold; font-size: 16px;"
  );

  if (
    !("google_tag_data" in window) ||
    !window.google_tag_data?.ics?.entries
  ) {
    console.log("No Consent Mode data found");
  } else {
    const consentEntries = window.google_tag_data.ics.entries;

    // Process each consent entry, except 'wait_for_update'
    for (const entry in consentEntries) {
      if (entry === "wait_for_update") continue;

      const defaultStatus = consentStatusString(
        consentEntries[entry]["default"]
      );

      const updateStatus = consentStatusString(
        consentEntries[entry]["update"]
      );

      console.log(
        `%c${entry}`,
        "font-weight: bold; font-size: 14px;"
      );

      // Default status
      if (defaultStatus !== "") {
        console.log(
          `\tdefault: %c${defaultStatus}`,
          consentStatusColor(defaultStatus)
        );
      } else {
        console.log("\tdefault: not found");
      }

      // Update status
      if (updateStatus !== "") {
        console.log(
          `\tupdate: %c${updateStatus}`,
          consentStatusColor(updateStatus)
        );
      } else {
        console.log("\tupdate: not found");
      }
    }

    // wait_for_update
    if ("wait_for_update" in consentEntries) {
      console.log(
        "%cwait_for_update",
        "font-weight: bold; font-size: 14px;"
      );

      const initialConfig = window.dataLayer?.find(
        item =>
          item?.[0] === "consent" &&
          item?.[1] === "default"
      );

      const waitForUpdate =
        initialConfig?.[2]?.wait_for_update ?? "not found";

      console.log(
        `\tdefault: %c${waitForUpdate}`,
        "color: #FFA500"
      );
    }
  }


  /* =========================================================
     MICROSOFT UET
     ========================================================= */

  console.log(
    "%cMicrosoft UET",
    "font-weight: bold; font-size: 16px;"
  );

  console.log(
    window.uetq ?? "No UET data found"
  );


  /* =========================================================
     MICROSOFT CLARITY CONSENT
     ========================================================= */

  console.log(
    "%cMicrosoft Clarity",
    "font-weight: bold; font-size: 16px;"
  );

  if (typeof window.clarity === "function") {
    clarity(
      "metadata",
      (d, upgrade, consent) => {
        console.log("consentStatus:", consent);
      },
      false,
      true,
      true
    );
  } else {
    console.log("No Clarity data found");
  }

})();

Issue breakdown

WORDS
4,610
Respectable amount of text.
HEADINGS
8
Loose structure.
READ TIME
23 min
You’ll survive.
LINKS
5
Paths to somewhere.
CODE BLOCKS
6
Mildly technical.
IMAGES
0
Also bold.

Want more from Karppinen.one?

Make Karppinen.one a preferred source on Google to see more of my articles in your searches.

Add Karppinen.one as a preferred source on Google