Show half of your new visitors one Cookie Consent layout or palette and the other half another, then count the answers each version collects. Cookie Consent has no built-in A/B testing, so a short script of your own picks the variant and passes its options to run(). The test covers the layout, the palette, and the placement, because no option changes the wording.

The setup takes 4 steps and about 45 minutes, plus a verification. You need the Cookie Consent block from the TermsFeed generator, an endpoint on your server that accepts a small JSON record, and enough new visitors to reach a result. Visitors who answered see the consent banner again only after their answer expires, so the test reaches new visitors and returning ones whose answer expired.

Pick a or b at random on the first visit and store the letter in a first-party cookie, so each visitor keeps one variant on every page. The cookie holds a single letter and no identifier, so it never tells one visitor from another.

The cookieConsentVariant() function reads the variant cookie and writes it when it is missing, with a lifetime of 30 days. Paste the function once, inside the configuration block that starts Cookie Consent.

function cookieConsentVariant() {
  var match = document.cookie.match(/(?:^|;\s*)cc_ab_variant=(a|b)(?:;|$)/);
  if (match) {
    return match[1];
  }
  var variant = Math.random() < 0.5 ? 'a' : 'b';
  document.cookie = 'cc_ab_variant=' + variant + '; max-age=2592000; path=/; samesite=lax';
  return variant;
}

Your script writes this cookie before the visitor answers, so check that your regulator allows it without consent. The French data protection authority (CNIL) exempts A/B testing cookies only under conditions, such as informing visitors, letting them object, and a lifetime of 13 months at most. The CNIL sets these conditions out in its sheet on analytics, and other regulators decide for their own countries. Read more about consent for analytics cookies in Tracking and Analytics Cookies.

Keep one set of shared options and add the options that differ per variant, such as notice_banner_type, palette, or notice_banner_append. Every other option, including consent_type and the categories, stays the same in both variants, so the result measures the change you made and nothing else.

This block replaces both script tags from the generator, the loader and the inline run() script. It picks the variant, merges its options, and starts Cookie Consent with the 2 callbacks that count each outcome.

<script type="text/javascript" src="https://www.termsfeed.com/public/cookie-consent/4.2.0/cookie-consent.js" charset="UTF-8"></script>
<script type="text/javascript" charset="UTF-8">
(function () {
  function cookieConsentVariant() {
    var match = document.cookie.match(/(?:^|;\s*)cc_ab_variant=(a|b)(?:;|$)/);
    if (match) {
      return match[1];
    }
    var variant = Math.random() < 0.5 ? 'a' : 'b';
    document.cookie = 'cc_ab_variant=' + variant + '; max-age=2592000; path=/; samesite=lax';
    return variant;
  }

  function report(variant, outcome) {
    var record = JSON.stringify({ variant: variant, outcome: outcome });
    navigator.sendBeacon('/consent-stats', new Blob([record], { type: 'application/json' }));
  }

  var variant = cookieConsentVariant();

  var variantOptions = {
    a: { "notice_banner_type": "headline", "palette": "light" },
    b: { "notice_banner_type": "simple", "palette": "dark" }
  };

  var options = {
    "consent_type": "express",
    "language": "en",
    "page_load_consent_levels": ["strictly-necessary"],
    "website_privacy_policy_url": "https://www.example.com/privacy-policy",
    "callbacks": {
      "notice_banner_loaded": function () {
        report(variant, 'shown');
      },
      "user_consent_saved": function (details, reason) {
        if (reason !== 'choice') {
          return;
        }
        var cc = cookieconsent.cookieConsentObject;
        var used = ['functionality', 'tracking', 'targeting'].filter(function (category) {
          return cc.isLevelInPurposes(category);
        });
        var accepted = used.filter(function (category) {
          return details[category] === true;
        });
        var outcome = accepted.length === used.length ? 'accepted_all'
          : accepted.length === 0 ? 'declined_all'
          : 'custom';
        report(variant, outcome);
      }
    }
  };

  var extra = variantOptions[variant];
  for (var key in extra) {
    options[key] = extra[key];
  }

  document.addEventListener('DOMContentLoaded', function () {
    cookieconsent.run(options);
  });
})();
</script>

Keep any tagged scripts and the <a href="#" id="open_preferences_center"> link from the generator block, placed after this script. A site that sends consent signals keeps its scripts_specific_loaded callback and callbacks_force: true in the same options object, beside the 2 counting callbacks.

A site that sends Google Consent Mode updates keeps its denied defaults as the first script in <head>, after only the charset meta. The defaults sit above the Google tags, the Google Tag Manager container, and the Cookie Consent block. That site puts its gtag('consent', 'update', ...) call in this user_consent_saved callback, before the reason check, and adds "user_consent_saved_on_page_load": true. The update then runs on every page, and the count runs once per answer.

Step 3: Store the outcome counts without identifying anyone

Count one "shown" record per page load with the consent banner and one outcome record per answer, each with the variant letter only. The notice_banner_loaded callback runs on each page load of a visitor who has not answered. The user_consent_saved callback with reason 'choice' runs once per answer.

Your endpoint stores 4 counters per variant: shown, accepted_all, declined_all, and custom. Do not store an IP address, user agent, consent token, or timestamp finer than a day, so the records never single out a visitor. Divide the answers each variant collects by its shown count to see how often each version gets an answer. Divide each outcome count by the answers of its variant to see how visitors answer.

navigator.sendBeacon hands the record to the browser, which sends it even when the visitor leaves the page right after answering. Decide the sample size and the end date before you start, and stop the test on that date.

Step 4: Keep both variants fair to visitors

Give both variants the same choices with the same effort, so the test compares design and never weakens consent. Apply these rules to the buttons, the consent type, the CSS, and the texts of both variants.

  • Both variants keep the "I decline" button. Leave notice_banner_reject_button_hide off in both, so refusing takes no more effort than accepting in either variant. Check the rules behind the reject button with Reject All Button and Cookie Walls.
  • Both variants use express consent. Testing implied against express consent changes what the answer means, so the test compares 2 consent rules instead of 2 designs.
  • Your CSS keeps the buttons equal. Leave the "I decline" button as visible as the "I agree" button in both palettes and every layout you test.
  • Both variants show the same texts and links. Keep website_name, website_privacy_policy_url, website_impressum_url, and notice_banner_insert_legal_urls identical in both variants.

The checks assume express consent with page_load_consent_levels left out or at the generator value ["strictly-necessary"], and the "I decline" button shown. Confirm that each variant appears, that the cookie keeps the variant, and that every answer sends one record. Publish your change and clear any page cache first. Close every private window, open a new one, open Chrome DevTools, and load your site.

  1. Select the Application tab, then Storage, Cookies, and your site. cc_ab_variant holds a or b, and the consent banner matches that variant.
  2. Select the Network tab and type consent-stats in the filter box. One request with "outcome":"shown" appears on each page load before the answer.
  3. Click the "I decline" button. One request with "outcome":"declined_all" appears, and no request repeats on the next page load.
  4. Close every private window and open a new one until the other variant appears, then check that it shows the other layout or palette.