Skip to content

Cookie banner

A cookie banner for your own site, outside Website Studio. One script line on your site, Accept and Reject the same size, and a record of every choice without an IP address.

DocumentationCookie banner

The public site previews leave space inside the browser frame. Long example banners can scroll within their own panel.

A cookie banner for a site that is not in Website Studio. You set it up here, paste one line on your site and then see what your visitors chose. A site from Website Studio carries its own banner; you do not need this one for that.

For cookies that are not strictly necessary, such as preferences, statistics and marketing, the Dutch Telecommunications Act (article 11.7a) requires consent first. The banner texts follow that. A banner helps you arrange this; whether your site meets all the rules otherwise remains your own call.

Creating a banner

Under Cookie banner (in Website Studio), choose Create banner. Give it a name and fill in the domains of your site, one per line (for example https://www.example.com). Only those sites may load the banner and send a choice. Another site that copies your line gets nothing.

One banner is one site. The www variant and your subdomains (such as shop.example.com) belong to the same site and the same banner; another domain (such as example.org) is another site and gets its own banner. Your plan covers one site; you can't create more banners than sites. There is no limit on the number of pages or visitors.

One setting for all your sites

Under Cookie banner there are three tabs: Sites, Setting and Log.

Under Setting you choose once the categories, the texts in Dutch and English, the colours (from your brand), the cookie statement, the default language and how long the proof is kept. It applies to all your sites. A new banner starts with that setting. When you change the setting, every banner that followed it receives the change; if that changes what your visitor sees (the categories, the texts or the language), we ask your visitors again.

A banner may differ, but it is always visible. On the Sites tab a banner shows what it does differently ("Differs from the setting: Texts, Retention period"), and on the banner's screen each item has a Follow the workspace button to let go of the difference. A difference stays when you change the setting: what you chose differently for one site is never overwritten. If you already had banners with different settings, nothing changed at the switch: the oldest banner became the setting, and every other banner keeps its own value and shows up as a difference in the list.

A site from Website Studio is in the list with its status (live, draft or paused). Its banner is part of the site itself and does not take the workspace setting over yet. A banner for a site elsewhere has the status Live, Off or, when your trial is over without authorisation, Strict: the script then shows no window, allows only necessary cookies and records nothing. With authorisation everything works again at once.

Setting it up

  • Categories. Necessary cookies are always on. You can offer or turn off preferences, statistics and marketing; a category that is off does not appear in the banner.
  • Texts in five languages. Dutch, English, French (for Belgium and France), German (for Germany and Austria) and Spanish. Under Texts per language you pick the language you edit. An empty field follows our default text, which is shown in the field as an example; in every language Accept and Reject carry the same weight. The French, German and Spanish default texts do not name any country's law: if you want that, put it in your own text. The banner follows the language of your page (nl, en, fr, de or es); anything else gets the banner's default language.
  • Appearance. See below.
  • Colours from your brand. Leave the colours empty and the banner follows your brand style. If a colour is not readable on its background, the banner picks black or white.
  • Cookie statement. A link to your own cookie page, over https.
  • Retention. How long the record of a choice is kept: 30 to 730 days, 395 by default.
  • Validity of the choice. How long a visitor's choice holds before the banner asks again: 30 to 365 days, 365 by default. You set this per banner (not for the whole workspace) and it is separate from the retention of the record. Longer than a year is not possible: even if an older copy of the script would do something else, the script uses 365 days at most. A different period does not ask your visitors again; it applies to the choices made after that, and an existing choice stops counting once it is older than the new period.

If the categories or texts change, we ask your visitors again. A different colour or appearance does not.

A preview while you set it up

Next to the form there is a preview: the real banner (the same script and the same styling) on a mock site, on desktop or mobile and in every language. The preview follows what you type. It runs in a sealed frame: nothing is sent to us and nothing is stored, not even when you click Accept. A logo from your brand or a newly chosen logo only shows after you save, and the preview does not show your brand font.

Appearance

Every option comes from a fixed list and applies to this one banner (not to the whole workspace).

  • Position. A bar at the bottom across the full width, a card at the bottom centre (the default), a card at the bottom left or bottom right (at most 420 pixels wide), or a window in the centre with a light overlay. In every position Accept and Reject are the same size and on the same layer. A click outside the centre window chooses nothing.
  • Logo. None, the logo of your brand, or a logo of your own. It sits to the left of the title, at most 32 pixels high, with the name of your banner as alt text. A logo of your own is a PNG, JPEG or WebP up to 200 kB (no SVG: that is a document that can carry script); we check the bytes, not the file name. You choose or upload it with the media picker and, for an upload, tick I am allowed to use these images. A chosen logo is saved and selected right away. If the site cannot fetch the image, for example because of its own security policy, the logo disappears and the title stays.
  • Font. The system font (the default), the font of your site (the script takes the font of the page), or the font of your brand. The last only works if your site already loads that font: the banner never requests a font itself, from us or from anyone else.
  • Corner radius. 0, 6, 12 (the default) or 20 pixels, or a pill (round buttons).
  • Reopening the choice. Three ways. An icon: a small round icon of 44 pixels, bottom left or bottom right, that appears after a choice and opens the settings panel (its label is the title of that panel, in the language of the page). Your own link (the default): no icon, you put a link or button with data-denkploeg-cookies="open" or data-cookie-instellingen on your site. Or off: we advise against it, because withdrawing consent must be as easy as giving it. In code you call window.DenkploegCookies.open(); it always works, even while the settings are still on their way (the panel then opens as soon as they arrive).

Language and accessibility

  • Switching live. If the language of your page changes without a reload (<html lang>, for example through a language picker), the banner fetches its texts in the new language and redraws an open banner or panel. At most one switch per second. A choice that was made and the ticks your visitor already set stay. If fetching fails, the current language stays.
  • Focus. On opening, focus moves into the banner, onto the title. Escape closes the settings panel and never the first layer; without a choice the banner comes back. The centre window and the panel keep Tab inside the window.
  • Contrast. A colour that is not readable on its background (below 4.5) is replaced by the banner with black or white.

On your site

Under On your site there is one line. Paste it in the <head> of your pages, as high as possible, above Google Tag Manager, gtag and other third-party scripts:

<script src="https://…/cookies.js" data-sleutel="dpc_…"></script>

Leave out defer and async (or choose Fast loading below). Only then does the script run before a Google tag loads and is Google Consent Mode v2 set to denied (ad_storage, analytics_storage, ad_user_data and ad_personalization set to denied). If your visitor chose before, the script applies that choice right away, without waiting for the network. An old line with defer keeps working, but then Consent Mode is only updated after the choice: too late for a Google tag that has already loaded. So put the new line in without defer.

  • Before the choice, the banner sets no cookie and loads nothing from anyone else. It holds back known third-party scripts itself (see below), and scripts that you mark wait for the choice. Google tags follow Consent Mode.
  • Accept and Reject are the same size and equally prominent. Settings opens a panel with a switch per category.
  • Mark a third-party script with type="text/plain" and data-categorie="statistiek" (or voorkeuren, marketing). It only runs after consent for that category. You do not need to mark a Google tag: it follows Consent Mode, which the embed line sets to denied itself and updates after the choice.

A marked script looks like this:

<script type="text/plain" data-categorie="statistiek" src="https://example.com/tracker.js"></script>

Holding back scripts without a mark (optional)

With data-autoblokkeren="aan" on the script line, the script itself holds back third-party scripts you do not mark until there is consent. The line you get for a new placement has that attribute; a line you already had works as it did. This covers scripts created with code (the Google Tag Manager loader, the Meta pixel) and scripts in the page that come after our script. A known address (Google Analytics, Meta, Hotjar, LinkedIn and a few others) waits for consent for its own category; Stripe, Mollie, PayPal, Klarna, Adyen and the Google and hCaptcha captchas always load. An unknown script from another domain counts as not necessary and waits for marketing; to make it always load anyway, put data-categorie="noodzakelijk" on it. Scripts of your own site always load. A general CDN (such as cdnjs or jsDelivr) is not automatically necessary.

This is not a full scanner. A script the browser already fetched before our script ran is not held back, and a script with inline text only stays closed with type="text/plain" and data-categorie. What we hold back loads with its attributes (such as integrity and crossorigin) and its onload, but not with listeners set via addEventListener. With no banner (off), if our address is down or if the trial has ended without a mandate, what we held back loads normally so a checkout does not break; scripts you marked yourself stay closed.

A script with inline text works too: put type="text/plain" and data-categorie on the script and keep the text.

Fast loading (async)

The standard line is synchronous: the browser waits briefly for the script, which is why Consent Mode is set to denied before a Google tag loads. If you want the script not to block your page, turn on the Fast loading (async) switch under On your site, at How to place it. You then get two parts, in this order:

<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){ dataLayer.push(arguments); }
  gtag('consent', 'default', {
    'analytics_storage': 'denied', 'ad_storage': 'denied', 'ad_user_data': 'denied',
    'ad_personalization': 'denied', 'functionality_storage': 'denied', 'personalization_storage': 'denied',
    'wait_for_update': 500
  });
</script>
<script async src="https://…/cookies.js" data-sleutel="dpc_…"></script>

The stub sets the six Consent Mode keys to denied itself, before anything else loads; the script then applies your visitor's choice as soon as it has loaded. The standard, synchronous line stays the default. One thing differs: with async an earlier choice only comes through when the script is ready, not at the very start of the page. Google waits for the update for the time in wait_for_update (500 milliseconds); a tag that fires earlier does so with the default denied. Test my site recognises the stub in front of an async line and approves it.

If you have a strict Content-Security-Policy with a nonce, put the same nonce on both scripts (<script nonce="…">). The script passes that nonce on to the scripts it activates after consent.

How to place it

Under On your site there is a guide per way of installing, each with the line to copy (and with the Fast loading switch):

  • Regular site and static HTML. Paste the line right after the <head>, above all other scripts.
  • WordPress and WooCommerce. Put the line in the Header field of a header code plugin (such as WPCode), or in header.php of your (child) theme, before wp_head(). WooCommerce uses the same header for shop, cart and checkout. If you have a cache plugin (WP Rocket, LiteSpeed Cache, W3 Total Cache) or Cloudflare, exclude cookies.js from delaying, combining and minifying JavaScript, turn Rocket Loader off or add data-cfasync="false" to the line, and clear the cache. Turn off any other cookie banner plugin.
  • Shopify. Paste the line in layout/theme.liquid, right after the <head>, above {{ content_for_header }}. See The Shopify bridge below.
  • Magento. Paste the line under Content, Design, Configuration, (your scope), HTML Head, Scripts and Style Sheets, then clear the cache. Magento puts this field at the bottom of the head; if a Google tag or Tag Manager already sits earlier in the head, move it here, below our line.
  • Next.js and React. Next.js loads a script asynchronously through the Script component, so use the variant with the stub: the stub in the <head> of the root layout, and the script with strategy="beforeInteractive". Your visitor's choice then comes through client-side: in the browser after the script has loaded, not in the HTML your server sends. An app with a fixed index.html (Vite, Create React App) uses the standard line in that <head>. A third-party script that should only run after consent, you load yourself after the denkploeg-cookies event (event.detail.categorieen).
  • Google Tag Manager. Our line (or stub and line) goes above it. See below.

The Shopify bridge

If window.Shopify is on the page, the script passes your visitor's choice on to Shopify's Customer Privacy API: it loads the consent-tracking-api feature and calls setTrackingConsent with analytics (statistics), marketing, preferences and sale_of_data (equal to marketing). Someone who withdraws their choice passes false everywhere. On a site without Shopify the script does nothing here.

That way Shopify apps and pixels can follow the choice our banner records, and you can turn off Shopify's own cookie banner. Turn it off only after you have seen the choice reach Shopify: check in your browser console with Shopify.customerPrivacy.currentVisitorConsent(). Shopify's checkout pages do not load the theme; our banner is not there.

Note: we have not yet verified the Shopify bridge on a real store. It follows Shopify's documentation, but in our tests it is only checked against a simulated Shopify API. Check it on your own store before you rely on it.

When our address is briefly down

  • A choice that does not arrive. If reporting a choice fails (a network error, a server error or a rate limit), the script tries once more after a few seconds. If it keeps failing, the script marks the choice in its own cookie (p) and reports it again on the next page visit with a valid choice. A reply from our server that makes resending pointless (400, 403 or 404) closes it. That way the record is not lost; our server counts a choice that arrives twice once.
  • Settings that do not arrive. The script tries fetching them twice more, with a delay. If that still fails, it shows the last good settings from your visitor's browser storage (at most seven days old; only texts, colours, categories and the version, never data about a visitor). If nothing was stored, a warning appears in the console and the denkploeg-cookies-fout event follows, which your site can catch. Scripts you marked stay closed then: when things fail, no tracker is ever opened. Consent Mode stays on the default denied.
  • An address that does not belong to the banner (a different key or domain, or a banner that is off) gets a definitive no: the script does not retry and shows nothing from storage either.

Test my site

Under On your site there is a Test my site button per domain. We read the HTML of your front page once and tell you whether this banner's line is in it, in the <head>, without defer or async (or with async after the Fast loading stub), and above Tag Manager and other scripts. We also name the third-party scripts and iframes that load before the choice and are not marked (Google tags are not: they follow Consent Mode). We only read the HTML as your server sends it: a script that adds another script later, we do not see. You can test at most ten times an hour, and we keep nothing of your page.

With Google Tag Manager

  1. Put our line (without defer) above the Tag Manager snippet in the <head>.
  2. The line sets Consent Mode to denied and updates it after your visitor's choice.
  3. In Tag Manager, turn on the built-in consent checks for each tag (analytics_storage for statistics, ad_storage for marketing) and do not let a tag fire without consent. Google's own tags often do this by themselves; for other tags you turn it on yourself.

We do not have a ready-made Tag Manager template yet. A script that you do not mark and that does not know Consent Mode is not blocked automatically either.

  • A link or button with the attribute data-cookie-instellingen reopens the settings, so a visitor can change their choice. Anyone who withdraws a choice gets the page reloaded.
  • A new key makes the old script line worthless. Do this if the line is somewhere it should not be.

Verifying your domain

We only scan a site that you have verified is yours. Otherwise someone could point our scanner at another person's site by entering that domain in their banner. To have your site scanned, set a DNS record. Under Cookies on your site you see per site (the main domain, so www and subdomains are included) whether it is verified:

  1. In your DNS management, create a TXT record on your main domain with the value the screen shows, denkploeg-verificatie=<your token>. The token belongs to this banner and this domain.
  2. Choose Check the DNS record. DNS can take a quarter of an hour to a few hours before it is visible.
  3. We check the record again every week. If it is gone, the proof lapses and we stop scanning.

A script alone is not proof. If the script line of the banner is on your site, we see that and show a hint (we see your script on this domain; set the DNS record to turn the scan on), because anyone who knows the key of a banner can imitate a request from another domain. The script does count for the passive observation: what your visitors report themselves.

Without a DNS record we do not scan and the Scan now button is off. You can always fill in cookies by hand and confirm your list. If your banner has more than one domain, we only scan the domains that are verified.

What is on your site: the scan

Under Cookies on your site we read your site and show what is there. That way you can make a cookie statement without hunting down the cookies yourself.

  • How we scan. We read the sitemap of your site (sitemap.xml, or the reference in your robots.txt, including a sitemap index) and load at most 50 pages, in a sandboxed browser that exists only for this work. For each page we write down which cookies appear, which storage the page uses and which known services scripts come from. We never write down a value, only names and domains. The scan takes a few minutes at most.
  • A sample per kind of page. If your site has more than 50 pages, we pick a few pages per template (the first part of the address, such as /blog/ or /product/) instead of the first 50. If the limit of 50 or your robots.txt leaves out pages, the scan is partial and the message says how many we read: we read 50 of the 400 pages. The list then does not count as fully scanned; you can confirm it yourself.
  • Only your site. We only load pages from the domains you entered at the banner. A reference in your sitemap to another domain is ignored.
  • Who we are. The scanner introduces itself as Denkploeg-Scanner/1.0 (+https://denkploeg.nl/docs/cookiebanner). That way you can recognise it in your log and allow it in your firewall.
  • When. First you set the DNS record (see Verifying your domain): without it we do not scan and Scan now is off. After that it can run right away with the Scan now button (at most once an hour), and we scan every month again and after a change to your sitemap (we check once a week whether it changed, reading your robots.txt first).
  • New cookies. If a later scan finds a cookie that was not there before, you get a notification.
  • Classification. A cookie we know is classified as necessary, preferences, statistics or marketing, with vendor and retention period. A cookie we do not know yet counts as marketing for your visitor until Denkploeg has classified it. We do that once for all customers: a classification then applies on every site.
  • Your own cookies. If a cookie of yours needs a different class, or one is missing, you add it yourself. What you classify stays; a scan does not overwrite it.

When the scan does not work

The banner tells your visitors: The list of cookies on this site has not been fully scanned yet. That stays so after a scan that was refused, failed or read only part of the site, and for a banner that has never been scanned. We do not pretend a list is right when we do not know that.

We do not scan a site that refuses scanners by going around it. If your robots.txt forbids scanners, your site answers the scanner with a 401, 403 or 429, or a firewall or captcha is in front of it, we stop and explain why. You have three options:

  1. Put our scanner on the allowlist. Allow traffic with the user agent above (in your firewall, in Cloudflare, in a security plugin). If you would rather list the scanner by IP address, ask us for the addresses. Then request a new scan. If you do not want to let the scanner in, leave robots.txt as it is: we respect it.
  2. Let the banner watch along. If you have the script line on your site, the banner reports the names of the cookies it sees on the page in about one in ten visits, after the visitor's choice. Only names and your domain, never a value, an id or a person, at most 50 names per report and a rate limit per site. What a script reports is a claim: it appears in your list as reported by a visitor and Denkploeg checks it before it applies to all customers. You can remove such a row yourself, and it expires after 90 days without a new report. We drop number and hex sequences from a name (a user id or a hash). An observation alone does not give you a notification about a new cookie.
  3. Fill in the cookies yourself and then choose My list is correct. That way you vouch that the list is complete, and the banner stops saying it is not.

A scan of your own site: what we can and cannot do

We see what a visitor gets without logging in, on the pages in your sitemap. Cookies that only appear after a login, a purchase or a certain action, we do not see; the passive observation fills that in partly. A scan is therefore no guarantee that your list is complete, and a cookie statement remains your responsibility.

The record of choices

Under Visitors' choices you see the last choice per visitor: how many visitors accepted everything, rejected everything and how many allowed statistics. Download the record gives a csv with, per choice, the time, a random id of the visitor, the choice per category, the banner version and a rough device class.

What we do not keep: the IP address and the user agent. The visitor id is a random number in a cookie on your own site, but it remains a pseudonymised personal data item and is not anonymous: you can link it to your own data, for example to a logged-in customer. Treat the export as personal data. After the retention period the record disappears. If you delete the banner, the record goes with it.

The record shows what the banner recorded when a visitor chose, and no more. It comes from the visitor's browser; anyone who deliberately sends noise can pollute it. That is why we store at most 20,000 choices per banner per day as separate rows. Above that the banner keeps working and your visitor's choice holds; we then only count those choices, and the platform gets an alert.

One log for all your sites

The Log tab shows the proof of all sites together, newest first: when a visitor chose, on which site or banner, which choice per category and which banner version. You can filter by site, period or a single visitor. For a visitor's (GDPR) request, search on their ID: it is in the dp_toestemming or dp_cookies cookie of their browser. Download the proof (csv) follows the filter and contains at most the latest 50,000 choices. The log reads the existing record: there is no second registration. Only the owner and administrators can download it, and not during the trial.

What your own security policy must allow

The script runs from our address on your site. If you use a Content Security Policy, allow our address in script-src (the script) and in connect-src (fetching the settings and sending the choice). Third-party scripts you put behind type="text/plain" only run after consent if your policy allows inline scripts or the address of that third party; otherwise your browser blocks them, even after a yes.

Installation help

Rather have us look along? With every plan that includes the cookie banner you can take installation and check-up help: together with you we place the banner and the assistant (or look along), check before and after Accept and Reject that the marked scripts wait, go through a first scan with you and set up the sources of the assistant. An appointment follows within 5 working days. It costs EUR 79 one-off excl. VAT and is free if you pay yearly. You choose it at checkout or afterwards under Subscription; see Your subscription. The help gets the banner placed and checked properly, but whether your site complies still depends on your markings, your cookie statement and your placement; the responsibility for that stays with you.

Who may do what

Everyone inside the workspace sees the banners, but only the owner and administrators (right cookies.beheren) change them, scan and download the evidence. The Cookie banner module comes with the Cookie banner package, and is also part of Website, Website with customer service, Business, Everything and the Assistant with cookie banner bundle. Each of those packages gives one site with a banner. The Cookie banner package has no credits: there is nothing in the module to spend them on. If your subscription already had them, you keep them until the end of your current period. Everything you set, renew or export appears in the History.

Via MCP and the director

An external agent with a workspace key can read the cookie banners and propose a change. The director gets the same tools, with the rights of the user who asks. Whatever a visitor gets to see always goes past a human.

  • Read: cookiebanners_lezen gives per banner the categories, colours, shape, language and terms, and the figures of the last 30 days (numbers only). Never the key of the banner and never the choice of a visitor. The name, the texts and the address of the cookie statement are text from outside: the agent reads them as data.
  • Propose a change (right cookies.beheren, owner and administrator): cookiebanner_wijzigen_voorstellen puts a card under Approvals and changes something only when a human signs. It can cover the name, on or off, the categories, the texts per language, the colours, the default language, the retention, how long a choice holds and the shape (position, font, rounding, reopening). Only what you name changes. If the choice or a text changes, the banner asks every visitor again.
  • Deliberately not via MCP: the domains, the cookie statement, the brand and the logo, the key of the banner, creating, scanning or deleting a banner. A domain is a destination and deleting wipes the record of the choices; a human does that on the screen.

See Connecting via MCP.