
Cookie Consent Banner & GDPR Consent Mode v2
GDPR cookie consent banner with Google Consent Mode v2, script blocking until consent, a tamper-proof log and presets for GA4 and Meta Pixel.
Available for Odoo 16.0, Odoo 17.0, Odoo 18.0, Odoo 19.0. Technical name bambooforge_consent_pro.
Cookie Consent Banner & GDPR Consent Mode v2
A complete consent-management platform (CMP) for the Odoo 18 website: per-category cookie banner with a preferences modal, Google Consent Mode v2 signals, marketing/analytics scripts held back until the visitor agrees, an audit-ready consent register, EU geo-targeting and one-click presets for the common trackers.
This page is the complete manual. Follow it top to bottom and you can install, brand the banner, wire your trackers, prove consent to an auditor and fix the common issues without contacting support.
Overview
Per-category banner — Essential (always on), Functional, Analytics, Marketing. Accept all / Reject non-essential / granular preferences, all equally easy, as the GDPR requires.
Google Consent Mode v2 — a static gtag('consent','default',…) emitted at the very top of <head> (before any Google tag) and consent update pushes on every choice, covering ad_storage, ad_user_data, ad_personalization, analytics_storage, functionality_storage and personalization_storage. Odoo's built-in GA4 integration is governed automatically.
Script blocking — register trackers as Managed Scripts and they are only injected after their category is granted. Visitors who already consented get the scripts server-rendered (no flicker, no lost pageviews). Hand-placed tags can use the <script type="text/plain" data-bf-consent="analytics"> pattern.
Consent register — every Accept / Reject / Custom / Withdraw choice is stored with a salted visitor hash (no personal data), granted categories, policy version and country. This is the proof an auditor asks for. One entry per distinct choice: re-submitting the same choice refreshes the existing entry instead of adding a row, while a genuine change of mind lands as its own entry (see The consent register below). Retention is automatic (13 months by default).
Geo rules — ask everyone, or ask only EU/EEA/UK/CH visitors and apply implied consent elsewhere. Unknown geolocation is treated as EU (the safe default).
Presets — Meta Pixel, TikTok, LinkedIn Insight, Hotjar, Microsoft Clarity ship inactive with a placeholder ID: activate, paste your ID, done.
Re-prompt on policy change — bump the policy version and every visitor is asked again; old cookies become invalid automatically.
The core single-choice cookies bar is switched off at install so only one banner ever shows.
Requirements
Item |
Detail |
|---|---|
Odoo |
18.0, Community or Enterprise. Only the website app is required. |
Python / JS |
No extra libraries. The banner is framework-free vanilla JS. |
GeoIP |
The EU-only mode uses Odoo's GeoIP resolution (request.geoip). Without a GeoIP database every visitor is treated as EU — compliant, never under-asking. |
Getting started
Install BambooForge Consent Pro. The banner is live immediately with sensible defaults; the core cookies bar is switched off.
Open Website ▸ Configuration ▸ Settings ▸ Consent Pro and pick the audience mode (everyone vs EU-only), banner texts and policy version.
Move your trackers into Website ▸ Configuration ▸ Consent Pro ▸ Managed Scripts — or activate a preset and paste your pixel ID.
Visit the site in a private window: choose, then check Consent Register — your choice is there.
Usage
Managing scripts
Each Managed Script holds a category, an optional external Script URL and/or Inline JavaScript. Both run only once the visitor grants that category — injected immediately after the choice (no reload needed) and server-rendered on later page views.
If you keep a tag hard-coded in a template or through the website editor, neutralise it like this and Consent Pro will activate it on consent:
<script type="text/plain" data-bf-consent="marketing">
fbq('init', '123456789');
</script>
Google tags (GA4, Google Ads) need no managed script when Consent Mode is enabled: keep using Odoo's built-in GA4 field — Google's own tags read the consent signals and stay dormant until granted.
The consent register
Website ▸ Configuration ▸ Consent Pro ▸ Consent Register lists every choice: timestamp, salted visitor key, action, granted categories, policy version, country. Filter and group by action/country/day; export to XLSX with the standard exporter for an audit bundle. Entries older than the per-website retention (Settings, default 13 months — CNIL guidance) are vacuumed automatically.
One entry per distinct choice. An entry is identified by website + visitor key + policy version + action + granted categories. When the same visitor submits exactly the same choice again — reloading the page, clicking Accept all twice, a script replaying the request — the existing entry is refreshed in place and no row is added. A genuine change of mind differs in the action or in the categories (accept-all, then withdraw; analytics on, then analytics off) and is therefore stored as its own entry, so the register still shows what changed and when.
This is deliberate: /bf_consent/save is a public endpoint, so without it anyone could inflate the register into unbounded growth by replaying one request. Deduplication keeps the register an accurate picture of the choices actually made — and retention is unchanged (13 months by default). One consequence worth knowing: a refreshed entry keeps the timestamp of the moment that choice was first recorded, so the register dates the choice, not the last page view that repeated it.
Changing your cookie policy
Bump Policy version in Settings. Existing consent cookies no longer match, so every visitor is prompted again and new register entries carry the new version.
Withdrawing consent
The floating 🍪 badge (and the window.bfConsent.open() JS API for custom footer links) reopens the preferences modal at any time, as the GDPR requires. Newly revoked categories stop loading from the next page view; Google tags react immediately via consent update.
Configuration
Setting (per website) |
Effect |
|---|---|
Consent Pro banner |
Master switch. Off = no banner, no scripts blocked, no logging. |
Audience mode |
Ask every visitor, or EU-only (implied consent elsewhere — scripts load, no banner, badge still available). |
Google Consent Mode v2 |
Emit default + update signals. Leave on if you use any Google tag. |
Reject button |
Shows Reject non-essential on the first layer. Keep it on for GDPR compliance. |
Floating badge |
The persistent 🍪 button that reopens preferences. |
Policy version / Retention |
Re-prompt trigger and register retention in months. |
Banner title / message |
First-layer texts, translatable per language. |
Access: website designers manage scripts and read the register; deleting register entries is reserved to administrators.
Troubleshooting
- Two banners show.
The core cookies bar was re-enabled. Switch Cookies Bar off in website settings — Consent Pro replaces it.
- My tracker fires before consent.
It is not registered: move the tag into Managed Scripts or wrap it in the text/plain pattern. Tags pasted into Website ▸ Custom head code run outside any CMP's control.
- GA4 shows no data after installing.
That is consent working: Google tags stay dormant until a visitor grants Analytics. Expect consented traffic only — that is the compliant baseline everywhere in the EU.
- Non-EU visitors still see the banner in EU-only mode.
Your server has no GeoIP database, so geolocation is unknown and the safe default applies. Install MaxMind GeoLite2 (see Odoo docs, --geoip-city-db) to enable the distinction.
- I consented twice but the register shows one row.
That is the deduplication: the same choice (same visitor, policy version, action and categories) refreshes its entry instead of adding a row. Change something real — withdraw, or untick a category — and the new choice appears as its own entry.
- The banner does not show for me.
You already chose (badge instead), or you are in the website editor (the banner hides while editing), or your cookie predates a policy bump on another browser profile.
- Does it slow pages down?
No measurable impact: the consent-default script is inline and static (cache-friendly), consented scripts are server-rendered, and the banner assets ship inside the normal website bundle.
FAQ
- Is this enough for GDPR/ePrivacy compliance?
It implements the technical requirements: prior blocking, granular opt-in, equal reject, withdrawal, proof of consent, re-prompt on change. Pair it with an accurate cookie policy page — legal texts remain your responsibility.
- Does it work with Odoo's GA4 integration?
Yes — that is the flagship case. Consent Mode v2 governs it without any extra configuration.
- Multi-website?
Yes: all settings, scripts (optionally shared), register entries and retention are per-website.
- Does any data leave my server?
No. No CDN, no external CMP service, no fees per pageview. The register stores a salted hash, never the raw IP.
- Can visitors change their mind?
Always — the floating badge or window.bfConsent.open() reopens preferences; revocation is logged like any other choice, as its own register entry. Only an identical re-submission is deduplicated onto the existing entry.
- Community edition?
Yes, CE and EE alike.
Upgrade
Back up the database.
Replace the module folder, restart, then:
odoo-bin -d <db> -u bambooforge_consent_pro --stop-after-init
Review the Changelog; bump the policy version if a new release adds categories you start using.
Uninstall
Apps ▸ BambooForge Consent Pro ▸ Uninstall.
Removed: managed scripts, the consent register, banner templates and settings fields. Visitors' bf_consent cookies simply expire.
Re-enable the core Cookies Bar in website settings if you still want a basic banner afterwards.
Remember: without a CMP your trackers fire unconditionally again — check your compliance posture before removing.
Changelog
18.0.1.0.1 (2026-08-15)
The consent register keeps one entry per distinct choice. Re-submitting the same choice (same visitor, policy version, action and granted categories) now refreshes the existing entry instead of adding a row; a genuine change of mind (different action or different categories — accept-all then withdraw, a category toggled) is still stored as its own entry, so the audit trail of what changed and when is preserved.
Rationale: /bf_consent/save is a public endpoint, and this keeps the register an accurate record instead of something a replayed request can grow without bound. Retention (13 months by default) is unchanged.
18.0.1.0.0 (2026-08-14)
Initial release: per-category banner + preferences modal, Google Consent Mode v2 (default in head + updates), managed scripts with prior blocking and server-side rendering after consent, text/plain unblock pattern, audit-ready consent register with salted visitor keys and automatic retention, EU/EEA/UK/CH geo mode, floating re-consent badge and JS API, presets for Meta Pixel, TikTok, LinkedIn, Hotjar and Clarity, policy-version re-prompt, multi-website support, browser-tested (frontend tour) with a full HTTP test suite.
Screens




