# A cookie banner that passes: reject button, lifetime, consent mode

> What a compliant cookie banner looks like in 2026: Reject next to Accept, cookie lifetimes of 13 months or less, Consent Mode v2 set to denied first, and how the cookie scanner checks each one.

Updated 2026-09-25 · Best practices · HTML version: https://getreport.app/guides/cookie-banner-that-passes-reject-button-lifetime-consent-mode

Most cookie banners fail in the same three places: no "Reject" next to "Accept", cookies that last two years, and Google tags that do not know what the visitor chose. Each is a setting in your consent platform or a few lines of code. This guide shows what a banner that passes looks like, how the cookie scanner tests it, and the order to set it up in. Plan an hour to configure, and a second hour if tags have to move into Tag Manager.

## Quick answer

- Put **Reject all** on the first layer, the same size and weight as **Accept all**, with a link to settings and to the privacy policy.
- Keep every consent-based cookie, including the one that stores the answer, at **13 months or less**.
- Set **Consent Mode v2** defaults to `denied` before any Google tag loads, and let the banner send the update.
- Block every other tracker until consent, then test with the scanner: nothing before the click, nothing after "Reject".
- Give visitors a way back: a "Cookie settings" link in the footer that reopens the banner.

## Why the banner's details matter

**The rules in plain terms.** In the EU, the ePrivacy rules say nothing may be stored on or read from a visitor's device before consent, unless it is strictly necessary for what they asked for. The GDPR defines consent: an active, informed, specific and free choice, as easy to withdraw as to give. Pre-ticked boxes and "by continuing to browse you agree" are not consent. The full legal picture, including the UK and the US, is in [cookies before consent](https://getreport.app/guides/cookies-before-consent).

**Refusing must be as easy as accepting.** Regulators in several countries have required a reject option on the first layer. The French CNIL fined Google and Facebook in 2022 because refusing took more clicks than accepting, and in the European Data Protection Board's 2023 cookie banner taskforce report most EU authorities agreed that a first layer with "Accept" but no way to refuse breaks the rules. A banner with "Accept" and a small "×" or "Settings" is the most common failure.

**Lifetimes should be proportionate.** The French CNIL's guidance, the benchmark most banners and getReport's check follow, puts the maximum at 13 months; after that, ask again. For audience measurement that is exempt from consent, the CNIL's conditions keep the cookie to 13 months and the data collected with it to 25 months. Google Analytics' `_ga` lasts two years by default.

**Consent Mode keeps the numbers usable.** When half your visitors click "Reject", your analytics lose half the data. Google's [Consent Mode](https://getreport.app/learn/consent-mode) tells Google tags what each visitor chose; in advanced mode, tags send cookieless pings when consent is denied, and Google uses them to model conversions and, on sites with enough traffic, behaviour in GA4. It does not replace the banner: without a banner asking, there is no consent to report.

## How getReport checks it

> **Free tool:** [Cookie and consent scanner (GDPR)](https://getreport.app/tools/cookie-scanner): See every cookie your page sets and every tracker it loads before the visitor answers the cookie banner, then what changes after "Accept". Categories, lifetimes, third parties, whether the banner offers a reject option, and the cookie flags.

The scanner loads the page in a fresh Chromium session, records every cookie and tracker request, finds the banner's accept button, clicks it, waits about 2.5 seconds and records what changed. Four findings come out of it that matter for the banner:

![The cookies table of the scanner on a fixture whose banner has no reject option: three first-party cookies, cookie_notice_accepted as necessary consent for 12 months, and _ga and _gcl_au as Google Analytics analytics cookies for 13 and 3 months, all set after accept, each with SameSite=Lax](https://getreport.app/guides/img/cookie-banner-that-passes-reject-button-lifetime-consent-mode/cookies.webp "Each cookie with its category, whether it appeared before or after the accept click, its lifetime and its flags.")

In that example, the tags wait for the click (every cookie says "after accept") and the lifetimes stay within 13 months. What fails is the banner itself: it offers no way to say no.

> **Check: Cookie banner reject option.** EU regulators (CNIL, the Datatilsynet, the EDPB guidance) expect refusing to be as easy as accepting: a "Reject all" or "Only necessary" control on the first layer, not hidden behind "Settings". A banner without it is a dark pattern and, in several countries, a finable one.
>
> 1. Enable the first-layer reject button in your consent platform (your consent platform: usually a "Show reject all" or "Decline" setting) with the same size and colour weight as accept.

An info finding with no points. It runs only when a banner was detected. The scanner looks for a visible reject control, first through the known selectors of 18 consent platforms (Cookiebot's "Decline", OneTrust's "Reject All", CookieYes, Complianz, Usercentrics, Didomi and others), then for any visible button or link whose whole label is a reject phrase in about 20 languages, searching inside the banner first: "Reject all", "Decline", "Only necessary", "Continue without accepting", "Alle ablehnen", "Tout refuser", "Odbij sve" and so on. A reject button hidden behind "Settings" is not visible on the first layer, so it does not count.

> **Check: Cookie lifetime.** French and Spanish guidance caps consent-based cookies at 13 months, and Chrome and Safari already trim long lifetimes. A two-year analytics cookie earns nothing and reads badly in an audit.
>
> 1. Set the cookie's Max-Age or Expires to 13 months or less (GA4: the cookie_expires parameter; Meta Pixel and most tags have a similar setting).

Also info. It counts every cookie the scan saw, before and after the click and whatever its category, and lists those that live longer than 397 days. Chrome caps lifetimes at 400 days, so a two-year cookie shows as 400 days and is still listed.

> **Check: Analytics run with Google Consent Mode.** Consent Mode tells Google tags what the visitor agreed to, so analytics keep working (in aggregate) after a "Reject" click instead of going dark. Without it, EU sites lose a large share of their measurement.
>
> 1. Enable Consent Mode v2 in your consent banner's settings and connect it to Google Tag Manager or the GA4 tag.

Info again, and only shown when the page runs an analytics or marketing tag (GA4, Tag Manager, Meta Pixel, Hotjar, Clarity, Matomo, LinkedIn, TikTok and a few others). It passes when an inline script calls `gtag('consent', …)` or the `dataLayer` holds a consent command or an event whose name contains "consent". It also appears for cookieless analytics such as Plausible, where Consent Mode is irrelevant; ignore it there.

> **Check: No trackers load before consent.** Analytics, ad pixels and session-recording scripts that run before the visitor answers the cookie banner set identifiers without permission. Under GDPR and ePrivacy that needs prior consent; regulators fine for it, and the banner is worthless if the tags do not wait for it.
>
> 1. Load tracking tags through your consent platform (Cookiebot, OneTrust, Complianz, CookieYes, Google Tag Manager with consent triggers) so they run only after "Accept". Test in a fresh incognito window: no tracker request until you click.
> 2. If you use Google tags, add Consent Mode v2 with the defaults set to denied; the tags then send cookieless pings until consent.

The one that costs points: a warning with weight 5. It matches requests made before the click against 25 analytics, advertising and session-replay trackers. When Consent Mode is present and no Google cookie was set before the click, Google's cookieless pings are not counted.

## Step by step

### 1. Take the inventory

Run the scanner on the home page and on one page per template (a product, an article, the contact page). Write down every cookie and tracker with its owner and category. That list drives everything else: the banner's categories, the blocking rules and the cookie section of your privacy policy.

If the list shows no analytics or advertising cookies and no trackers, you may not need a banner at all. Session, cart and security cookies do not require consent.

### 2. Choose a consent platform

| Type | Examples | Good for |
| --- | --- | --- |
| Open source, self-hosted | Klaro, tarteaucitron.js, CookieConsent (MIT licence) | Developers who want no third-party script and full control |
| Hosted platforms | Cookiebot, Usercentrics, CookieYes, Iubenda, OneTrust | Sites that want scanning, policy text and consent logs handled; several offer a free plan for small sites, with limits on pages or visits |
| WordPress plugins | Complianz, CookieYes, Cookie Notice | WordPress sites; the free versions on wordpress.org cover a basic banner and script blocking |

Whatever you choose, check four settings before anything else: a first-layer reject button, Consent Mode v2 support, script blocking by category, and the lifetime of the consent cookie.

### 3. Design the first layer

A banner that passes has, on the first screen:

- **Two equal buttons**: "Accept all" and "Reject all", same size, same colour weight. Grey text for reject next to a bright accept button is the pattern regulators object to.
- **A "Settings" or "Choose" link** that opens per-purpose toggles, all off by default.
- **One or two sentences** naming the purposes ("analytics to count visits, marketing to measure ads") and linking to the privacy policy.
- **No cookie wall**: the page stays readable while the banner is open.

The answer is remembered, and a "Cookie settings" link in the footer reopens the banner so visitors can change their mind as easily as they gave consent.

A minimal banner of your own, for a site whose only tags are Google's:

```html
<!-- 1. First script in <head>, above gtag.js or Google Tag Manager -->
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('consent', 'default', {
    ad_storage: 'denied', ad_user_data: 'denied',
    ad_personalization: 'denied', analytics_storage: 'denied',
    wait_for_update: 500
  });
  if (/(?:^|; )site_consent=granted/.test(document.cookie)) {
    gtag('consent', 'update', {
      ad_storage: 'granted', ad_user_data: 'granted',
      ad_personalization: 'granted', analytics_storage: 'granted'
    });
  }
</script>
```

```html
<!-- 2. End of <body>: the banner, hidden once a choice exists -->
<div id="consent-banner" role="region" aria-label="Cookie consent" hidden>
  <p>We use analytics and marketing cookies only if you agree.
     <a href="https://getreport.app/privacy-policy/#cookies">What we use</a></p>
  <button type="button" id="consent-reject">Reject all</button>
  <button type="button" id="consent-accept">Accept all</button>
</div>
<script>
  (function () {
    var banner = document.getElementById('consent-banner');
    if (!/(?:^|; )site_consent=/.test(document.cookie)) banner.hidden = false;
    function choose(granted) {
      var v = granted ? 'granted' : 'denied';
      gtag('consent', 'update', {
        ad_storage: v, ad_user_data: v, ad_personalization: v, analytics_storage: v
      });
      // 180 days, well inside the 13-month limit
      document.cookie = 'site_consent=' + v + '; Max-Age=15552000; Path=/; SameSite=Lax; Secure';
      banner.hidden = true;
    }
    document.getElementById('consent-accept').addEventListener('click', function () { choose(true); });
    document.getElementById('consent-reject').addEventListener('click', function () { choose(false); });
  })();
</script>
```

Style both buttons with the same class. A footer link that deletes `site_consent` and shows the banner again gives visitors their way back. This only covers Google's tags; any other tracker must be loaded after "Accept", which is what consent platforms automate.

### 4. Block everything else by category

Non-Google tags ignore Consent Mode. In Tag Manager, require consent in each tag's Consent Settings or fire it on your platform's consent event; in the page's HTML, use the platform's `type="text/plain"` pattern with a category attribute. [Cookies before consent](https://getreport.app/guides/cookies-before-consent) has the code for Cookiebot and OneTrust, and [GA4 and Tag Manager: the cost and the consent](https://getreport.app/guides/ga4-and-tag-manager-the-cost-and-the-consent) covers the Google side in depth.

### 5. Set lifetimes

Shorten Google Analytics in GA4 (Admin → Data streams → your stream → Configure tag settings → Override cookie settings) or in the tag:

```js
// gtag.js config (in Tag Manager: the Google tag's cookie_expires parameter), value in seconds
gtag('config', 'G-XXXXXXXXXX', { cookie_expires: 395 * 24 * 60 * 60 });
```

In your consent platform, set the consent cookie's lifetime to 6 to 12 months; the check counts it like any other cookie.

### 6. Test before the click and after reject

Run the scanner again: no trackers before consent, the reject finding names your button, no cookie over 13 months. Then test "Reject" by hand, because the scanner only clicks accept: a new private window, open DevTools, click "Reject all", reload, open a second page. The Application tab should show no `_ga`, `_fbp` or similar cookies, and the Network tab no tracker requests beyond Google's cookieless pings.

### The speed and UX cost

A consent platform is one more script on every page, with its configuration, and it has to run before your tags. Load one, not two. Show the banner as a fixed overlay at the bottom of the screen: an overlay does not move the page, while a banner inserted at the top of the document pushes everything down and shows up as layout shift. On mobile the banner text is often the largest element on screen, so it can become the page's Largest Contentful Paint; keep it short and use the page's own fonts. [Fix Cumulative Layout Shift](https://getreport.app/guides/fix-cumulative-layout-shift) covers the shift side.

## Platform notes

### WordPress

Complianz, CookieYes and Cookie Notice all add a reject button through their banner settings; check it is switched on and visible on the first layer. Plugins that print their own tags (analytics, pixels, chat) need the consent plugin's integration or the WP Consent API, or they fire before consent regardless of the banner.

### Shopify

The built-in banner (Settings → Customer privacy) offers accept and decline. Pixels added under Settings → Customer events follow the choice; code pasted into the theme does not.

### Static sites and custom builds

Load order is what matters: consent defaults first, then the consent platform, then Tag Manager, with every other tag inside Tag Manager.

## Verify

- The scanner shows "The cookie banner offers a reject option", "All … cookies expire within 13 months", "Analytics run with Google Consent Mode" and "No trackers load before consent".
- After "Reject all" by hand, no analytics or advertising cookies appear on the next two pages.
- The footer's "Cookie settings" link reopens the banner, and changing the answer takes effect without clearing cookies.
- The privacy policy lists the same cookies and services as the scanner; see [privacy policy and contact links](https://getreport.app/guides/privacy-policy-and-contact-links-the-trust-signals).

## Common mistakes

- **Accept and an ×, nothing else.** Closing the banner is not refusing, and the scanner does not treat an × as a reject control. Add "Reject all" on the first layer.
- **Cookies set by the theme or a plugin before consent.** The banner is configured, but a hard-coded pixel in the header fires anyway. The trackers finding names it; remove the copy outside the consent platform.
- **Consent stored for five years.** The cookie holding the answer is subject to the same 13-month guidance, and the lifetime finding lists it. Set 6 to 12 months.
- **A banner on a site with nothing to consent to.** If the scan shows only necessary cookies and no trackers, the banner is friction without purpose. Remove it, or switch to cookieless analytics.
- **A banner the scan cannot see.** Banners drawn inside an iframe, shown only after a long delay, or hidden from headless browsers by a firewall rule leave the scan with the before-consent state only. The trackers result is still valid; check the reject button by hand.
