> ## Documentation Index
> Fetch the complete documentation index at: https://docs.37audits.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Cookie Consent

> Loads the page in a real browser and checks for a cookie consent banner that offers both accept and reject choices.

<Info>
  **Security Auditor** — renders the audited page in a headless Chromium, looks for a visible cookie consent banner and checks that it lets visitors both accept and reject non-essential cookies.
</Info>

<CardGroup cols={2}>
  <Card title="Privacy" icon="shield-halved">
    Visitors in the EU, the UK and many other places must be asked before non-essential cookies are set, and a real choice means a reject button, not only an accept one.
  </Card>

  <Card title="Rendered pages" icon="browser">
    Consent banners are injected by JavaScript, so the page is loaded in a real browser, the network is allowed to go idle and the accept button is clicked to count the cookies it sets.
  </Card>
</CardGroup>

A cookie consent banner is the first thing many visitors see, and the law in a growing number of jurisdictions says it has to offer a choice: accept the non-essential cookies, or reject them and keep browsing. A banner that only has an accept button, or a notice with no button at all, collects no valid consent, and a site with no banner that still sets tracking cookies is exposed to complaints and fines. This auditor loads the page the way a visitor does, finds the banner through the class names, ids and roles that consent tools use, reads the labels of the buttons on the page, and reports what choice the visitor gets.

## How it works

1. **Render** — launches a headless Chromium as 37AuditsBot, navigates to the audited URL, waits for the network to go idle and then two more seconds so consent scripts can inject their banner. A blocked navigation (HTTP 403, HTTP 429 or a Cloudflare challenge) ends the audit with `590`; a navigation that returns no response or an HTTP status of 400 or more ends it with `500`; a browser failure, after one relaunch when Chromium crashes, ends it with `501`.
2. **Find the banner** — queries sixteen common selectors (`[class*='cookie']`, `[id*='consent']`, `[class*='gdpr']`, `[role='dialog']`, `.cookie-banner`, `#cookieConsent` and the like) and takes the first visible match per selector as the banner; hidden elements do not count. The text and the button labels of each banner are recorded.
3. **Find the choices** — reads the text of every `button`, `a[role='button']` and `input[type='button']` on the page and looks for accept keywords (`accept`, `allow`, `agree`, `ok`, `enable`, `yes`, `continue`), reject keywords (`reject`, `decline`, `deny`, `disable`, `no`, `refuse`, `essential only`, `necessary only`) and settings keywords (`settings`, `preferences`, `customize`, `manage`, `options`, `choose`). Links whose text or `href` mention `privacy`, `policy`, `gdpr` or `data protection` count as a privacy policy link.
4. **Count the cookies** — records the cookies present after the page loaded, flags likely tracking cookies (`_ga`, `analytics`, `track`, `_utm`, `facebook`, `google`) set before any consent, and, when a banner with an accept option exists, clicks the accept button, waits two seconds and counts the cookies again.
5. **Rules** — no visible banner fails (`400`); a banner with an accept option but no reject option (`300`), with a reject option but no accept option (`301`) or with neither (`302`) warns.
6. **Aggregate** — FAIL wins over WARNING; when the banner offers both choices the audit is `SUCCESS` (`200`) and the message names the mechanism, the buttons and the cookie counts before and after accepting.

## What it audits

Every row maps to one `checkRule*` method and one `CheckCode`, in the order the auditor runs them. CheckCode suffixes use status ranges: `200` success, `300–399` warning, `400–499` fail, `500–599` error.

| CheckCode | Status | When it fires | Recommendation | Why it matters |
| - | - | - | - | - |
| <a id="37A-CookieConsentAuditor-400" />`37A-CookieConsentAuditor-400` | `FAIL` | No visible cookie consent banner was found on the rendered page. | Add a cookie consent banner to `{url}` that is visible on the first visit and offers both an accept and a reject choice; `{count}` cookies (`{cookieNames}`) set before any consent. | Visitors in the EU, the UK and many other jurisdictions must be asked before non-essential cookies are set; a site that never asks risks privacy complaints, regulator fines and the trust of its visitors. |
| <a id="37A-CookieConsentAuditor-300" />`37A-CookieConsentAuditor-300` | `WARNING` | A banner is visible and the page has an accept button but no reject button. | Add a "Reject" or "Essential only" button next to the accept button of the banner on `{url}` (buttons today: `{buttons}`) so visitors can decline non-essential cookies as easily as they accept them. | A banner that only lets visitors accept is not a real choice: privacy regulators treat that consent as invalid, and privacy-conscious visitors leave rather than agree. |
| <a id="37A-CookieConsentAuditor-301" />`37A-CookieConsentAuditor-301` | `WARNING` | A banner is visible and the page has a reject button but no accept button. | Add a clear "Accept" button next to the reject button of the banner on `{url}` (buttons today: `{buttons}`) so visitors who want analytics or personalisation can opt in. | Without a clear accept choice visitors cannot opt in to analytics or personalisation, so the site loses measurement and has no consent record for the cookies it sets. |
| <a id="37A-CookieConsentAuditor-302" />`37A-CookieConsentAuditor-302` | `WARNING` | A banner is visible but the page has neither an accept nor a reject button. | Add both an "Accept" and a "Reject" button to the banner on `{url}` (buttons today: `{buttons}`) so visitors can make a real choice about non-essential cookies. | A notice visitors cannot act on collects no consent at all, so every non-essential cookie it covers is set without permission while the banner still gets in the way. |
| <a id="37A-CookieConsentAuditor-200" />`37A-CookieConsentAuditor-200` | `SUCCESS` | All rules passed: a visible banner with both accept and reject options. Message names the mechanism, the buttons and the cookie counts, for example `The cookie consent banner on {url} offers both accept and reject options (Binary Choice; buttons: "Accept All", "Reject All"); 1 cookie set before consent and 3 cookies after accepting.` | — | — |
| <a id="37A-CookieConsentAuditor-500" />`37A-CookieConsentAuditor-500` | `ERROR` | The browser navigation returned no response, or the page answered with an HTTP status of 400 or more that is not a block. | Make sure `{url}` answers with HTTP 2xx, then run the audit again. Last error: `{error}`. | — |
| <a id="37A-CookieConsentAuditor-501" />`37A-CookieConsentAuditor-501` | `ERROR` | The headless browser failed to render the page (a navigation error, a timeout or a crash), after one relaunch on a crash. | Make sure `{url}` loads in a headless Chromium browser, then run the audit again. Last error: `{error}`. | — |
| <a id="37A-CookieConsentAuditor-590" />`37A-CookieConsentAuditor-590` | `ERROR` | The audited page answered HTTP 403, HTTP 429 or a Cloudflare challenge, so 37AuditsBot was blocked before it could read the page. | Allow 37AuditsBot in your firewall or bot protection: allowlist its user agent, or verify its Web Bot Auth signature. See [https://www.37audits.com/bot](https://www.37audits.com/bot). | While 37AuditsBot is blocked this audit cannot look at the site, so problems on it go unreported. |

Every check carries the data the browser collected: `hasConsentBanner`, `hasAcceptOption`, `hasRejectOption`, `hasSettingsOption`, `consentMechanism` (`None`, `Granular`, `Binary Choice`, `Accept Only` or `Banner Only`), `hasPrivacyPolicyLink`, `bannerText`, `availableOptions`, `detectedBannerSelectors`, `cookiesBeforeConsent`, `cookiesAfterAccept`, `cookiesAfterReject`, their three counts and `blocksNonEssentialByDefault`.

## Output documentation

| Status | Description | Test logic |
| - | - | - |
| **SUCCESS** | The banner offers a real choice | A visible consent banner, an accept button and a reject button (`200`). |
| **WARNING** | The banner does not offer both choices | Accept but no reject (`300`); reject but no accept (`301`); neither (`302`). |
| **FAIL** | No consent banner | No visible element matched any consent selector after rendering (`400`). |
| **ERROR** | The auditor could not read the page | No navigation response or HTTP status of 400 or more (`500`); browser render failure (`501`); site blocked 37AuditsBot (`590`). |

## Risks and considerations

### Why consent matters

* The ePrivacy Directive, the GDPR and the UK PECR require consent before non-essential cookies (analytics, advertising, social media) are set, and regulators have said that consent is only valid when rejecting is as easy as accepting.
* Consent collected through an accept-only banner can be declared invalid, which turns every cookie set under it into an unlawful one.
* Similar rules exist outside Europe (Brazil's LGPD, several US state laws), so the banner is rarely optional for a site with international visitors.

### How the detection works, and its limits

* The banner is found by selector: an element whose class or id contains `cookie`, `consent`, `gdpr` or `privacy`, or that has `role="dialog"` or `role="banner"`. A site header with `role="banner"` or a privacy link with `privacy` in its class is matched too, so a page without a consent banner can read as having one.
* The accept, reject and settings buttons are looked up on the whole page by keyword, not only inside the banner: an unrelated `OK` or `No` button counts, and a banner whose buttons use other words (`Got it`, `I understand`) is not recognised.
* The page is rendered with the bot's user agent from one location, so a banner shown only to EU visitors, or hidden from bots, is not seen.
* Only the accept button is clicked. The reject path is not exercised, so cookies set after rejecting are not measured; `cookiesAfterReject` repeats the cookies seen before consent.
* Granular settings (`Manage preferences`) are recorded in `hasSettingsOption` and the `Granular` mechanism name but do not change the verdict.

### Changes from the previous version

* A page that answers HTTP 400 or more is now an `ERROR` (`500`) instead of being audited like any other page; a browser navigation that returns no response is also `500`.
* A browser failure that survives the relaunch is an `ERROR` with code `501` instead of a code-less check carrying the raw Playwright message.
* Every check now carries the audited URL as its resource and a `CheckCode`; the verdict severities (no banner fails, a banner missing a choice warns, both choices pass) are unchanged.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.