WordPress consent
that blocks before the click.
OW Consent neutralises trackers in the HTML before the visitor has chosen, applies the legal regime of their jurisdiction, and writes every decision into a hash-chained ledger you can verify. Eleven compliance profiles, nine named regimes — from GDPR to CCPA. Free, GPL, no Pro version.
Why another consent plugin?
The banner was never the problem. Almost every site has one, and the trackers still leave on the first byte: the Analytics script is hard-coded into the theme, the ad pixel runs from the footer, the YouTube iframe drops its cookies while the banner is drawn on top. Clicking “Accept all” then authorises nothing at all — it ratifies what has already happened.
The second trap is caching. As soon as a plugin decides the HTML by reading the visitor's cookie, it stops working behind a page cache — or worse, it serves one visitor's choices to another. The third is proof: Article 7(1) asks you to demonstrate consent, and a box ticked somewhere in the database demonstrates nothing whatsoever.
OW Consent starts from those three constraints. The markup served is the same for everyone, neutralised trackers included, and it is the browser that releases what the visitor has granted. Every decision is appended to a signed hash chain. And the regime applied — opt-in or opt-out, “Do Not Sell” link, binding GPC signal or not — follows the jurisdiction, not a default setting.
In strict mode, on by default, the document is rewritten on its way out: scripts neutralised to text/plain, iframes switched to about:blank behind a clickable placeholder, pixels replaced with a transparent GIF, connection hints removed. A visitor who never clicks never loads those resources.
Nothing on the rendering path reads the cookie. The visitor's state is rebuilt in the browser and stamped onto the html tag before the first paint. LiteSpeed, WP Rocket, Varnish or a CDN can cache the page without ever mixing two visitors up.
Every event joins a chained table, every row signed with HMAC together with the hash of the one before it, the head anchor kept outside the table. And if the ledger refuses to write, the API answers 503, cancels the cookie and releases no tracker: consent a site cannot prove is consent it does not claim.
What actually changes
No slogans. Here are the technical decisions that set this product apart.
In strict mode — on by default — the whole document is captured on its way out and rewritten in five passes: scripts become type text/plain with their URL parked in data-owc-src, iframes switch to about:blank behind a placeholder whose “Accept” button names the vendor, measurement pixels are replaced with a transparent 1×1 GIF, preconnect and dns-prefetch are removed (a mere hint already opens a TLS connection and hands over the IP), and third-party media already in the catalogue lose their autoplay. The shipped catalogue holds 175 signatures for 71 vendors, and you can add, fix or delete them from the admin. By default an unknown third-party script is let through, an unknown iframe is blocked.
Nothing the server renders depends on the visitor's cookie: the same neutralised tags are served to everyone, and the runtime restores them in the browser, in document order, for the granted categories only. The state is stamped onto the html tag before the first paint, so there is no banner flicker. The write token is fetched fresh before every submission, never embedded in cached HTML: fetching it is capped at 4 seconds, and its absence never blocks a choice.
GDPR, UK PECR, the Swiss nFADP, Québec's Law 25, LGPD, CCPA, POPIA, PIPL, India's DPDP, plus an Australian profile and a generic US one. The profile is not decorative: it decides the legal model (opt-in, or granted until refusal for CCPA, generic US and Australia), whether the “Do Not Sell or Share” link appears — one click actually executes the opt-out, it does not open a panel — whether the Global Privacy Control signal is binding, and both the language and the supervisory authority cited in the generated documents. Country detection exists, and is off by default.
Every acceptance, refusal, change or withdrawal joins a dedicated table: the row is signed with HMAC-SHA-256 (or SHA3-256) together with the hash of the one before it, and the head anchor is kept outside the table so that a truncation shows. What goes in: a pseudonymous browser token, the truncated IP, a user-agent fingerprint, the page, the profile in force, the exact revision of the banner that was displayed and the fingerprint of the linked policies — never a name, never an address. One button verifies the whole chain and names the break; the CSV or JSON export carries an envelope that lets a third party re-verify an extract. Default retention: 5 years.
Shipped switched off, turn it on whenever you like: the scanner walks your own pages on WP-Cron (25 URLs by default, adjustable up to 500), interleaving the home page, pages, posts, post types and taxonomies. Three distinct, traced sources: the HTML served, the Set-Cookie headers — the only cookies a server-side scan can prove, and the only ones nothing can block — and an in-page probe reserved for logged-in administrators, which catches what a tag manager injects at runtime. A finding becomes a catalogue rule in one click. And the report says what it did not read instead of passing a partial sweep off as an inventory.
Four documents generated as versioned WordPress pages — cookie policy, privacy policy, legal notice, terms and conditions — in the language of the jurisdiction (French, English, Portuguese), with the supervisory authority and the tracker table taken from the scan. Publishing is refused while a legally required field is empty, and every document carries a notice reminding you to have it reviewed. On the rights side, the owc_dsar_form shortcode opens seven request types, verified by an email round trip; the Article 12(3) deadline starts at identity confirmation, and the request opens a native WordPress request alongside it so that the export or the erasure covers every plugin on the site.
Against alternatives
Verifiable data. Only checking what actually exists in the competition.
| Criterion | OW Consent | Complianz | CookieYes | Borlabs Cookie |
|---|---|---|---|---|
| Business model | Free, GPL-2.0-or-later | Freemium (paid Pro) | Freemium (paid Pro) | Not stated |
| License key to activate | None | For the Pro version | For the Pro version | Not stated |
| How trackers are blocked | Server-side HTML rewriting, 175 signatures shipped and editable | Not stated | Not stated | Not stated |
| Chained, verifiable proof ledger | HMAC-SHA-256 chain, anchor kept outside the table, one-click verification | Not stated | Not stated | Not stated |
| Compliance profiles shipped | 11 profiles, 9 named regimes | Not stated | Not stated | Not stated |
| Configurable consent banner | 4 positions, light / dark / auto theme | |||
| Data subject request portal | Included: form, email verification, Art. 12(3) deadline | Not stated | Not stated | Not stated |
| Where recorded consents live | In your own hosting database, no third-party service | Not stated | Not stated | Not stated |
Three ways to embed a form
No proprietary page builder forced on you. Pick the approach that fits your stack.
<!-- What your theme prints -->
<script src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<iframe src="https://www.youtube.com/embed/VIDEO_ID"></iframe>
<!-- What the visitor gets before any consent (strict mode, the default) -->
<script type="text/plain"
data-owc-cat="analytics"
data-owc-vendor="Google"
data-owc-src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<span class="owc-embed-placeholder" data-owc-cat="social" data-owc-vendor="Google">
<span class="owc-embed-text">Google content blocked: this content uses cookies…</span>
<button type="button" class="owc-embed-accept" data-owc-cat="social">Accept Google</button>
<iframe data-owc-cat="social"
data-owc-src="https://www.youtube.com/embed/VIDEO_ID"
src="about:blank"></iframe>
</span>
<!-- This markup is the same for everyone, page cache included.
It is the runtime, in the browser, that restores the tags of the
granted categories, in document order. Withdrawing consent reloads
the page: a script that has already run cannot be unloaded. -->Reasonable doubts, direct answers
Yes, and the blocking lives in the markup, not in a promise. In strict mode, on by default, the document is captured on its way out and five passes rewrite scripts, iframes, pixels, connection hints and third-party media. Two limits you should know about: actual blocking rests on the catalogue of 175 signatures and on the inline code signatures — an unknown third-party script is allowed by default, an unknown iframe is blocked — and if you switch strict mode off, only the scripts registered by WordPress are handled, not the ones hard-coded into the theme. Finally, a document larger than 8 MB, or a failure of the regular expression engine, is served intact rather than broken.
Yes, that was the constraint it started from. The HTML served is identical for every visitor, neutralised trackers included, and the consent state is rebuilt in the browser from the cookie, then applied before the first paint. No cache can therefore serve one visitor's choices to another. The trade-off is explicit: the visitor's regime is decided on the client side, so without JavaScript nothing is recorded. A visitor without JavaScript sees a noscript block, loads nothing non-essential, and is never locked behind a window they cannot close.
It decides the legal model: opt-in for GDPR, UK PECR, the Swiss nFADP, Law 25, LGPD, POPIA, PIPL and DPDP; granted until refusal for CCPA, the generic US profile and Australia. It also decides whether the “Do Not Sell or Share” link is shown (CCPA and generic US only), whether the GPC signal is binding (the same two profiles: elsewhere the signal is reflected on screen but nothing is recorded), and both the language and the supervisory authority of the generated documents. Country detection is optional and off by default; it fails closed, meaning towards the strictest regime. Two limits we own up to: no state-by-state granularity in the United States and none province by province in Canada, and the MaxMind file is neither downloaded nor uploadable by the plugin — you drop it into uploads/ow-geoip/ yourself.
It proves exactly as much as the key that signs it, and the plugin tells you so plainly. Every row is signed with HMAC together with the hash of the one before it, and the head anchor is kept outside the table, so an edit, a truncation or a wipe all show up at verification. But if AUTH_KEY and AUTH_SALT are not in your wp-config.php and you have not defined OWC_LEDGER_KEY, the key comes from the database: anyone with database access can re-sign the chain. In that case the plugin shows an admin warning and reports for itself that the chain is no longer tamper-evident, instead of carrying on claiming otherwise.
No, and that is better said before you commit than after. The module implements TCF v2.2 in the browser — the __tcfapi API, an encoded TC string, the euconsent-v2 cookie — but it does not supply a CMP ID: that is assigned by IAB Europe once you register, and without it the module emits strictly nothing. The scope is bounded too: Core segment only, no publisher restrictions emitted, two special features supported, no global scope. In plain terms it behaves as an unofficial CMP-compatible mode, which is enough for many integrations but does not replace an official registration — Google AdSense in particular refuses it in production in the EEA. The module is off by default.
Deactivating touches no data: it merely removes the scheduled tasks. On deletion, two things always go, whatever your setting: the scheduled tasks and the data subject request table, the only table holding directly identifying data about other people that nothing bounds once the plugin has gone. The rest — consent ledger, scanner findings, tracker catalogue — survives, unless you have explicitly ticked full erasure, which is unticked by default: your proof of consent under Article 7(1) should not disappear with an uninstall.
No Pro version, no license key.
OW Consent is published under the GPL-2.0-or-later license. Everything this page describes is in the .zip you download: no gated module, no subscription, no limit on the number of pages, consents or requests.
- Everything is included: banner, tracker blocking, scanner, editable catalogue, chained ledger, document generator, request portal, Consent Mode v2, TCF v2.2 and Global Privacy Control.
- The .zip downloads from optionweb.dev: install it through Plugins → Add New → Upload Plugin. WordPress 6.2 minimum — below that the plugin refuses to start and says so, rather than showing a banner that blocks nothing.
- No third-party service, no license check, no tracker added: consents, requests and documents stay in your own hosting database.
- The code is GPL-2.0-or-later, readable and modifiable, with its extension points documented: the owc_settings, owc_scripts_catalog, owc_blocker_allowlist and owc_policy_template filters, plus the ledger actions.