Title: OW Consent
Author: OptionWeb
Published: <strong>October 16, 2026</strong>
Last modified: October 16, 2026

---

Search plugins

![](https://ps.w.org/ow-consent/assets/banner-772x250.png?rev=3750102)

![](https://ps.w.org/ow-consent/assets/icon-256x256.png?rev=3750102)

# OW Consent

 By [OptionWeb](https://profiles.wordpress.org/optionweb/)

[Download](https://downloads.wordpress.org/plugin/ow-consent.2.0.8.zip)

 * [Details](https://wordpress.org/plugins/ow-consent/#description)
 * [Reviews](https://wordpress.org/plugins/ow-consent/#reviews)
 *  [Installation](https://wordpress.org/plugins/ow-consent/#installation)
 * [Development](https://wordpress.org/plugins/ow-consent/#developers)

 [Support](https://wordpress.org/support/plugin/ow-consent/)

## Description

OW Consent is a consent management suite for WordPress. Eleven compliance profiles
ship with it, from the GDPR and the UK PECR to the CCPA, the LGPD, PIPL and Quebec
Law 25; the one in force decides the consent model, the Consent Mode defaults and
the documents generated.

This version requires WordPress 6.2 or later. Sites on 6.0 or 6.1 are not offered
the update.

#### Works behind a full-page cache

Nothing on the front end depends on the visitor’s consent cookie: the HTML is identical
for every visitor, and the consent state is rebuilt in the browser from the cookie
and applied before first paint. Trackers are blocked in the markup for everyone 
and released client-side for the categories the visitor granted, so a page cache(
LiteSpeed, WP Rocket, Varnish, a CDN) cannot serve one visitor’s choices to another.

#### What it includes

 * Cookie banner: four positions, light, dark or automatic theme, “Reject all” as
   prominent as “Accept all”, keyboard operable, translatable.
 * Tracker blocker: scripts, inline snippets, iframes, pixels, resource hints, analytics
   stylesheets and media, from 183 editable signatures.
 * Consent ledger: every action appended to a hash-chained table signed with HMAC-
   SHA-256, with the profile and the banner actually shown.
 * Data-subject requests: shortcode form, e-mail verification, the Article 12.3 
   deadline, WordPress’s own exporter and eraser hooks.
 * Legal documents: a privacy notice and a cookie policy per profile, in that profile’s
   language, fed by a site scanner that sweeps your own pages on WP-Cron, off by
   default.
 * Google Consent Mode v2, IAB TCF v2.2, Global Privacy Control, geo routing, OW
   Forms integration: each off unless you enable it.

#### Limits, stated plainly

The generated documents are drafting aids, not legal advice: have them reviewed 
before publishing. The scanner reports what your HTML contains plus what an administrator-
only probe observed; it is not a completeness guarantee. The TCF module is not a
CMP registered with IAB Europe and offers no vendor-level choice, so vendors may
reject its signal. The ledger is tamper-evident only while its signing key lives
outside the database, and warns you when it is not.

Each module is documented in the FAQ below.

### External services

OW Consent contacts one external service, and only when you enable the feature that
requires it.

#### IAB Europe Global Vendor List

Used by the IAB TCF v2.2 module, which is disabled by default. The TCF framework
requires the current list of registered ad-tech vendors; the plugin downloads it
so it can build a TC String and expose the vendor set through `__tcfapi`.

When it happens: only when the “IAB TCF v2.2” option is enabled, from a daily WP-
Cron job (or from the settings screen when you enable the option). It is never fetched
while a visitor’s page is being rendered, and the public `owco/v1/tcf/gvl` REST 
route serves the cached copy only — it never triggers an outbound request.

What is sent: nothing. The request is a plain HTTP GET of a public JSON file at `
https://vendor-list.consensu.org/v3/vendor-list.json`. No site data, no visitor 
data, no personal data and no identifier is transmitted; the only information the
service receives is what your server necessarily discloses by making an HTTP request(
its IP address and its user agent). The response is stored in a WordPress transient
for one week and served from that cache; if a refresh fails, the previously cached
list is kept.

Service provided by IAB Europe. Terms of use: https://iabeurope.eu/general-terms-
use/ — Privacy policy: https://iabeurope.eu/privacy-policy/

#### Requests to your own site

The site scanner issues HTTP requests to your own site’s public URLs in order to
inventory the trackers your pages load. These requests never leave your own host:
the URL list is restricted to your site’s host after the `owco_scanner_urls` filter
has been applied. No third party is involved.

#### MaxMind GeoLite2

If you place a GeoLite2 country database in `wp-content/uploads/ow-geoip/` and enable
the option, the plugin reads that local file with its own bundled reader. It never
contacts MaxMind, and it never sends an IP address anywhere: the lookup happens 
on your server and the address is discarded immediately.

#### Tracker host names shipped as blocking signatures

The plugin ships a list of third-party tracker host names — Google Tag Manager, 
Meta, and so on — in `data/tracker-catalog.json`. They are blocking signatures. 
A host matching one of them is a host the plugin _prevents_ the page from requesting
until the visitor has consented to the matching category; the plugin makes no request
to any of them, at any point, from the server or from the browser. The same list
is what the browser-side guard compares an injected script against before refusing
it, and what the generated cookie policy names as vendors.

The only host the plugin itself ever contacts is the Global Vendor List documented
above, and only when the IAB TCF v2.2 module is enabled.

## Installation

 1. Install the plugin from Plugins > Add New > Upload Plugin, or upload the `ow-consent`
    folder to `/wp-content/plugins/`.
 2. Activate it through the Plugins menu.
 3. Open the **OW Consent** entry in the WordPress admin sidebar. The whole plugin 
    lives on that single screen, in tabs: Dashboard, Banner, Compliance, Legal identity,
    Categories, Policies, Scanner, Tracker catalogue, Audit ledger and DSAR requests.
 4. Set your jurisdiction and consent model on the **Compliance** tab, then fill in
    the **Legal identity** tab. Those fields are what the generated documents are built
    from, and generation is refused while a mandatory one is empty.
 5. Generate your documents from the **Policies** tab, and have them reviewed before
    you publish them.
 6. Optionally, add `[owco_dsar_form]` to a page to open the data-subject request portal,
    and `[owco_dnsmpi]` where you want the “Do Not Sell or Share” control.
 7. Recommended: define `OWCO_LEDGER_KEY` in `wp-config.php`, so the consent ledger’s
    signing key lives outside the database. The Audit ledger screen shows a freshly
    generated line to paste.
 8. If your site sits behind a CDN, a load balancer or any reverse proxy, declare it
    with `OWCO_TRUSTED_PROXY` in `wp-config.php` (the plural `OWCO_TRUSTED_PROXIES`
    is accepted as an alias), giving the proxy addresses or CIDR ranges. The rate limiting
    on the public REST endpoints keys on the client address; when every request arrives
    from the same proxy address and the proxy is not declared, the plugin cannot tell
    one visitor from another and falls back to a per-visitor split backed by a wider
    site-wide ceiling, which is a coarser limit.

## FAQ

### Does the plugin work with a page cache?

Yes, and it is designed around that. The front-end HTML it produces is identical
for every visitor; consent is read from the cookie in the browser and applied before
the page is painted. Trackers are blocked in the cached markup and released client-
side. Only genuinely per-visitor responses — the DSAR verification page and REST
responses containing personal data — are marked uncacheable. No nonce is printed
in cacheable HTML either; the banner fetches one from a no-store endpoint before
each write.

### Does this plugin replace a paid CMP like OneTrust or Cookiebot?

For most sites it covers the same ground: banner, blocking, audit log, data-subject
requests, geo routing and generated documents. Two things it does not do: it is 
not a CMP registered with IAB Europe (see the TCF question below), and it does not
provide legal advice or reviewed legal documents — the generator produces drafts
that you must have reviewed.

### Is the ledger tamper-proof?

It is tamper-evident, under one condition. Each row is signed with HMAC-SHA-256 
over the previous row’s hash, so a modified or deleted row breaks the chain and 
verification reports where. The guarantee rests on the signing key being outside
the database, which holds when you define `OWCO_LEDGER_KEY` in `wp-config.php`. 
Without it, the plugin signs with a random key it keeps in the database: the chain
is still built and checked, but an attacker with database access could re-sign it,
and the admin tells you so. The key is never derived from WordPress’s authentication
keys and salts.

### Does the scanner slow down my site?

No. It is off by default, and once enabled it runs as a WP-Cron job at the frequency
you choose (weekly by default), fetching a sample of your own pages server-side.
It never runs during a visitor’s page view. Note that WP-Cron only fires when your
site receives traffic, or when you have set up a real cron task; on a site with `
DISABLE_WP_CRON` and no system cron, the scheduled scan will not run.

### Does the scanner find cookies?

Partly. The scheduled server-side sweep reads the `Set-Cookie` headers your pages
return, so it finds cookies set by the server, including HttpOnly ones, as well 
as scripts, iframes, stylesheets and pixels. It executes no JavaScript, so cookies
written by scripts in the browser are not visible to it; those are collected by 
a diagnostic probe that by default runs only for logged-in administrators. Treat
the result as an inventory of what was seen, not as an exhaustive list — and both
screens say so rather than leaving you to assume otherwise: the Scanner tab states
whether the last sweep finished, how many pages it read, how many it could not fetch
and whether a browser has ever reported back, and the generated cookie policy repeats
the same reservations to the reader.

### How does a data-subject request get fulfilled?

The visitor submits the form and receives a verification e-mail. Confirming the 
request on the linked page starts the Article 12.3 deadline and opens a WordPress
personal-data request. From the DSAR screen you can download a JSON export produced
by every exporter registered on the site (OW Consent’s own records, WordPress core’s,
and any other plugin’s), run every registered eraser, and close the request with
a written response that is stored with it. What the export and the erasure cover
depends on which plugins on your site register those hooks; anything that registers
none has to be handled by hand.

### Does it integrate with OW Forms?

Yes, when OW Forms is active and the integration is enabled. Every stored submission
is linked to the consent in force at that moment: the pseudonymous browser token,
the ledger entry that was current, the categories granted, the compliance profile,
the hash of the published documents, the timestamp, and the exact wording of the
consent checkbox that was accepted. Erasure requests cascade to OW Forms submissions
and their uploaded files, matched on the e-mail hash OW Forms itself stores, and
OW Forms submissions are included in WordPress’s Export and Erase tools.

Three limits are worth stating: no consent event is written into the ledger for 
a submission (the link references the entry already in the chain); submissions recorded
before 1.2.0, and submissions made while OW Forms is configured not to store them,
have no link; and the e-mail address remains stored in clear text by OW Forms, because
it is needed to answer the sender — the hash is a lookup key, not a privacy measure.

### Can I use the TCF module for Google AdSense or Ad Manager?

Only with your own CMP ID issued by IAB Europe, and even then with a caveat. Without
a CMP ID the module does not load at all. With one, it emits a correctly encoded
TC String and the panel displays every purpose and special feature that string can
claim, but TCF policy requires a registered CMP to present purpose-level and vendor-
level choices of its own; here the purpose switches follow the categories and there
is no vendor-level choice, so it is not a registered CMP and vendors are entitled
to reject its signal. If TCF-gated advertising revenue matters to your site, use
a certified CMP.

### What language are the generated documents in?

The privacy notice and the cookie policy are generated for all eleven profiles in
the jurisdiction’s own language: English, French for France, Belgium, Luxembourg
and Quebec, Portuguese for Brazil. The legal notice and the terms exist only in 
French, built around French law; for any other jurisdiction the generator refuses
to produce them rather than publishing an inappropriate document.

### Is the interface itself translated?

The interface is written in English and is entirely translatable: every string goes
through gettext under the `ow-consent` text domain, and `languages/ow-consent.pot`
ships as the template. No translation is bundled with the plugin. Locale packs are
built on translate.wordpress.org and installed and loaded by WordPress itself, which
is how a plugin hosted here is meant to be translated; to have the interface in 
your language, contribute or review the strings for that locale there.

### What can I change about the banner?

Four positions (bottom bar, bottom card, centre modal, top bar), a light, dark or
automatic theme, an optional site logo (your custom logo, falling back to the site
icon), configurable labels and a floating re-open button. Any link or button carrying
the class `owco-open-preferences` reopens the preferences panel.

“Reject all” is rendered with the same prominence as “Accept all”. The close cross
is disabled by default; when you enable it, closing the banner is recorded as a 
refusal of every optional category, never as a silent dismissal. It is present in
the markup either way, hidden while the setting is off, and it is revealed and relabelled
when a refusal cannot be recorded at all: a visitor whose choice the server will
not accept is not left facing a banner they cannot dismiss, and that dismissal is
not counted as a choice. The panel is keyboard operable, uses `role="switch"` toggles
with a visible on/off state, traps focus in modal mode and restores it on close.
Banner copy is translatable and can be replaced through the `owco_banner_texts` 
filter.

### What exactly does the tracker blocker block?

Third-party scripts, inline tracking snippets, iframes, tracking pixels (including
inside `noscript`), analytics and marketing stylesheets and media sources are rewritten
before consent and released afterwards, in document order. Resource hints (`preconnect`,`
dns-prefetch`, `preload`) aimed at a catalogued third party are dropped outright
rather than deferred, since a hint opens a connection and has nothing to restore.
Stylesheets and web fonts served by a host classified as functional are left alone:
neutralising them costs every first-time visitor a fallback-font page and buys no
consent. Lazy-loading attributes used by caching plugins are neutralised so a lazy
loader cannot restore a blocked URL. Blocked embeds are replaced by a placeholder
carrying a per-category unblock button.

The plugin ships 183 detection signatures in `data/tracker-catalog.json` (118 URL
patterns and 65 cookie names). The catalogue is editable from the Tracker catalogue
screen — add, edit, delete, search, plus a view listing the rules that cannot match—
and extensible through the `owco_scripts_catalog` filter. Unknown third-party scripts
are allowed by default and unknown third-party iframes are blocked by default; both
are settings, and a host allowlist (also exposed as the `owco_blocker_allowlist`
filter) always wins.

### What is recorded in the consent ledger?

Every consent action (accept, reject, partial save, GPC opt-out, renewal, withdrawal)
is appended to a hash-chained table. Each row’s HMAC-SHA-256 covers the previous
row’s hash and is written inside a transaction, so concurrent writes cannot fork
the chain. Rows store a pseudonymised IP (IPv4 last octet zeroed, IPv6 truncated
to /48), a user-agent hash, the page URL, the compliance profile, a hash of the 
legal documents in force, a fingerprint of the banner actually displayed and the
plugin version — the elements needed to reconstruct what the visitor was shown, 
for GDPR Article 7(1) accountability.

Chain verification walks the whole chain, reports the number of rows actually checked
and describes each break individually, including a key rotation, which it distinguishes
from a rewrite. Rows written before 1.2.0 carry an unkeyed digest that anyone with
database access can recompute; verification counts them separately and refuses to
declare a chain intact while any of them remain, because an unkeyed digest is not
evidence.

### What safeguards are on the data-subject request form?

The shortcode form (`[owco_dsar_form]`) collects access, rectification, erasure,
portability, restriction, objection and CCPA opt-out requests. Submitting it requires
an explicit declaration that the right is being exercised over the requester’s own
data, checked server-side and recorded with the request. The requester receives 
a verification e-mail; the link opens a confirmation page, and the request is only
confirmed by an explicit action on that page, so a mail security scanner following
the link cannot confirm an identity on the subject’s behalf. The token is single-
use, stored only as a SHA-256 hash, and expires after a configurable number of days(
7 by default).

Verification starts the Article 12.3 clock (30 days by default, configurable from
1 to 30) and opens a matching WordPress core request. Reminders are sent seven days
before the deadline and once it has passed, and the one-off two-month extension 
permitted by Article 12.3 notifies the data subject of the reasons. A request whose
author never confirmed their identity cannot be exported, erased or recorded as 
honoured: the check is enforced on the server, not only by hiding the buttons, so
nobody can assemble or destroy the record held about an address on the strength 
of a claim that was never proved.

### What does the legal document generator produce?

A privacy notice and a cookie policy for each of the eleven compliance profiles,
written against that jurisdiction’s own statute and in that jurisdiction’s language.
The legal notice and the terms exist as French documents only; generation for a 
non-francophone profile is refused with an explicit error rather than publishing
a French document to an English-speaking audience.

Documents are built from the settings on the Legal identity tab, and generation 
is blocked with the list of the fields still empty rather than publishing blank 
clauses. The cookie policy lists what the scanner actually found, with the vendor
and a link to that vendor’s own privacy policy. Unless you turn it off, every document
ends with a notice stating that it is an automatically generated template that must
be reviewed by a qualified professional before publication, with the generation 
date and the plugin version. Bodies and variables can be replaced through the `owco_policy_template`,`
owco_policy_vars` and `owco_policy_html` filters.

The tracker table states what the sweep behind it actually covered, in the document’s
own language: whether a sweep has ever run, whether the last one could read no page
at all, whether it was interrupted and after how many pages, how many pages could
not be fetched, whether any browser has ever reported what the pages load at runtime,
and the window beyond which a tracker no longer seen is dropped from the list. A
finding the blocker cannot suppress — a cookie set by a `Set-Cookie` response header—
is marked as such and the clause claiming that unclassified trackers are set only
after consent carries the matching exception. An incomplete sweep is disclosed rather
than published as a finished inventory.

### How do I know what the scanner’s findings actually cover?

Once enabled, the scanner runs on the WP-Cron schedule you choose — hourly, twice
daily, daily or weekly (weekly by default). A sweep fetches a sample of your own
public pages server-side (25 by default) and records third-party scripts, inline
tracking signatures, iframes, stylesheets, tracking pixels and the cookies named
in `Set-Cookie` response headers. Each finding records where it came from and whether
the blocker can actually act on it, and can be classified into the blocking catalogue
in one click.

The Scanner screen carries a coverage panel stating what the current findings rest
on: whether a sweep has ever run, whether the last one finished or was interrupted
by its time budget, how many pages it actually read, how many it could not fetch
and the first error returned, and whether a browser has ever reported back through
the probe. It is built from the stored status of the last sweep, so it is as accurate
after a scheduled run as after a manual one, and a run that reached no page is shown
as a failure rather than as a clean result. That is the panel to read before publishing
a cookie policy from these findings.

### Which Google Consent Mode v2 signals are emitted?

All seven: `ad_storage`, `ad_user_data`, `ad_personalization`, `analytics_storage`,`
functionality_storage`, `personalization_storage` and `security_storage`. The six
consent categories (necessary, functional, analytics, marketing, preferences, social)
are mapped to them from a single source of truth, so the banner, the blocker and
the page bootstrap cannot disagree. The default call is printed as static markup
and the update is computed in the browser, which keeps the page cacheable.

### What does the IAB TCF v2.2 module do?

It exposes `__tcfapi`, the `__tcfapiLocator` iframe and a TC String encoder, and
writes the `euconsent-v2` cookie only after the visitor has acted — never on a first
view. It requires a CMP ID assigned to you by IAB Europe: with no CMP ID the module
refuses to load at all, rather than emitting a string carrying CMP ID 0, and it 
refuses to invent a vendor-list version when the Global Vendor List has not been
cached yet. The vendor list is stored in a transient, refreshed by a daily cron 
job, kept for a week, and served stale rather than re-fetched when a refresh fails.

When the module is enabled, the preferences panel shows one switch per TCF purpose
the string can actually claim on this site and one checkbox per special feature 
the publisher has declared, so nothing is encoded into the string that was not displayed.
A special feature stays off unless it is ticked; “Accept all” does not turn it on.

### How are the CCPA opt-out and Global Privacy Control handled?

A “Do Not Sell or Share My Personal Information” control is injected in the footer,
or placed with the `[owco_dnsmpi]` shortcode. The first click records the opt-out
directly, as California Regs 7026(a)(1) requires, rather than merely opening the
preferences panel. On a site configured under a US profile the control is visible
for everyone, with no JavaScript involved. When geo routing is enabled, the same
markup is served to every visitor and revealed in the browser for those whose own
jurisdiction requires it, so a mandatory link is never decided by whichever visitor
happened to populate the page cache. Sites with geo routing off render nothing extra.

The Global Privacy Control signal (the `Sec-GPC` header and `navigator.globalPrivacyControl`)
is treated as a binding universal opt-out under the CCPA and US state profiles: 
the opt-out is recorded once per browser session, and the visitor is told when it
replaced a choice they had previously saved. Under GDPR-family profiles GPC is treated
as a hint — optional categories are pre-denied in the interface and the banner is
still shown — because consent there must be an affirmative act.

### How does geo-based profile routing work?

It is optional and off by default. When enabled, the visitor’s country is resolved
from Cloudflare `CF-IPCountry`, CloudFront `CloudFront-Viewer-Country`, a server-
set `GEOIP_COUNTRY_CODE`, or a local MaxMind GeoLite2 country database read by a
reader bundled with the plugin (no Composer package and no vendor directory required).
Proxy headers are trusted only when the site declares the proxy with `OWCO_BEHIND_CLOUDFLARE`,`
OWCO_BEHIND_CLOUDFRONT` or `OWCO_GEO_TRUSTED_HEADERS`; without a trusted source 
and without a database, detection deliberately fails closed to the strictest opt-
in profile. The IP address is used for the lookup and immediately discarded — never
stored, never logged.

Geo routing does not make the HTML vary. The page carries both consent models — 
the opt-in defaults and the opt-out defaults — and the browser selects the one matching
the visitor’s jurisdiction before the first Consent Mode default call is emitted.
The country itself is resolved on a request that reaches PHP and cached in `owco_geo`
for 24 hours, so a visitor whose very first page is served by the cache is treated
under the site’s configured profile until that cookie exists.

### Which compliance profiles are available?

Eleven: GDPR, UK PECR, Swiss nFADP, LGPD, POPIA, PIPL, DPDP, Quebec Law 25, CCPA,
Australian Privacy Act and a generic US state profile. The profile drives the consent
model (opt-in, or opt-out for CCPA, the generic US profile and Australia), the Consent
Mode defaults, whether the “Do Not Sell or Share” control is shown, whether GPC 
is binding, which privacy notice and cookie policy are generated, which rights are
listed and which supervisory authority is named.

### Which cookies does the plugin set itself?

All of them are first-party, none of them is used for tracking or analytics, and
they are declared in the generated cookie policy.

 * `owco_consent` — the visitor’s choices, their pseudonymous browser token and 
   the profile in force. Lifetime follows the renewal setting (12 months by default,
   capped at 13). A renewal setting of 0 is treated as that 13-month maximum, not
   as “never expires”, and the same figure governs the cookie, the server-side expiry
   check and the duration the generated documents publish.
 * `owco_geo` — the detected country and the resulting profile, for 24 hours. Written
   only when geo routing is enabled and a country was actually detected. It contains
   no IP address.
 * `owco_gpc` — a session marker recording that the Global Privacy Control signal
   has already been processed for this browser, so the signal is not re-recorded
   on every page view.
 * `owco_gpc_notice` — five minutes, so the interface can tell the visitor that 
   GPC replaced the choice they had saved.
 * `euconsent-v2` — the IAB TCF consent string, written only when the TCF module
   is enabled and only after the visitor has acted.

### What happens to my data if I delete the plugin?

The consent ledger, the scanner findings, the tracker catalogue, the OW Forms consent
links, the options and the settings survive deactivation and deletion by default.
Two things are removed whatever the “Delete all data on uninstall” setting says.

The scheduled jobs are always unscheduled, so deleting the plugin cannot leave a
cron event pointing at code that is gone. And the data-subject request register —
the names, e-mail addresses and free-text messages people sent you, plus its rate-
limiting transients — is always dropped. It is the only table holding directly identifying
personal data about other people; once the plugin is gone nothing enforces the retention
period that bounded it, and no screen is left that could read, answer, export or
erase it. Export the requests you need to keep from the DSAR screen before you delete
the plugin.

For a clean removal of everything else, enable “Delete all data on uninstall” before
deleting the plugin — it then drops the plugin’s tables, options and transients,
on every site of a multisite network.

## Reviews

There are no reviews for this plugin.

## Contributors & Developers

“OW Consent” is open source software. The following people have contributed to this
plugin.

Contributors

 *   [ OptionWeb ](https://profiles.wordpress.org/optionweb/)

[Translate “OW Consent” into your language.](https://translate.wordpress.org/projects/wp-plugins/ow-consent)

### Interested in development?

[Browse the code](https://plugins.trac.wordpress.org/browser/ow-consent/), check
out the [SVN repository](https://plugins.svn.wordpress.org/ow-consent/), or subscribe
to the [development log](https://plugins.trac.wordpress.org/log/ow-consent/) by 
[RSS](https://plugins.trac.wordpress.org/log/ow-consent/?limit=100&mode=stop_on_copy&format=rss).

## Changelog

#### 2.0.8

The browser now rejects a consent cookie signed with a key the server no longer 
accepts, as the server already did. Cookies written before the 2.0.5 key change 
were still honoured in the browser; those visitors are now asked again.

#### 2.0.7

The policy links appended to the footer menu are passed through `wp_kses_post()`
on return.

#### 2.0.6

Plugin URI and Author URI point straight at the plugin’s page and OptionWeb’s English
home, without a redirect. The OptionWeb entries of the tracker catalogue linked 
to missing pages; they now link to OptionWeb’s privacy policy, installed catalogues
included.

#### 2.0.5

The consent ledger, the consent cookie and every other keyed hash no longer use 
the WordPress authentication salts. The ledger key is `OWCO_LEDGER_KEY` when defined,
otherwise a random key the plugin keeps; rows signed before the update are attested
under it, so later edits are still detected. Visitors see the banner once more. 
The DSAR and CCPA shortcodes escape their output on return.

#### 2.0.4

JSON exports keep `wp_json_encode()`‘s default escaping.

#### 2.0.3

Styles and scripts ship as files under `assets/`; only JSON configuration stays 
inline. The scripts that must run first are excluded from optimiser deferral.

#### 2.0.2

Two `phpcs:ignore` annotations corrected.

#### 2.0.1

 * The autoloader still used the old prefix’s length, so the lazily-loaded classes
   resolved to files that do not exist: policy pages, DSAR submissions and every`/
   owco/v1/*` route failed with a fatal error since 2.0.0. Treat consent writes 
   from that window as unrecorded.
 * The 2.0.0 migration missed the OW Forms consent-link table, so OW Forms submissions
   stopped recording their consent link. It is now migrated.

#### 2.0.0

**Renames everything the plugin owns** from `owc` to `owco` (the directory requires
a prefix of at least four characters). Existing installations are migrated automatically.
Custom code hooking `owc_*` filters or CSS targeting `owc-*` classes must be updated.

 * Security: `GET /owco/v1/nonce` no longer mints a `wp_rest` nonce, which authorises
   writes across the whole REST API and could be harvested cross-origin. It now 
   mints a token scoped to this plugin’s own write routes and refuses a foreign-
   origin request.
 * The CCPA footer control is now printed through `wp_kses()`. The admin stylesheet
   has its own handle instead of core’s `wp-admin`.

#### 1.5.0

Scripts and stylesheets go through the WordPress asset API instead of being echoed.
Translations are no longer bundled. The unused `GET /owc/v1/state` route is removed.
Admin notices are confined to the plugin’s own screens.

#### 1.4.3

Fixed the consent and geo cookies being percent-encoded twice, which made every 
browser discard the choice while server-side checks kept passing. Verified end to
end on PHP 7.4 and 8.3.

#### 1.4.2

Fixed the 1.4.1 cookie reader failing on a valid cookie — same double-encoding cause
as 1.4.3, caught here for the read path. Update if you installed 1.4.1.

#### 1.4.1

Fixed consent writes being lost on a site whose visitors browse a different host
than the Site Address setting (www vs. apex, an alias, a proxy). The banner now 
re-anchors its REST call on the origin actually browsed.

#### 1.4.0

Public write endpoints no longer depend on WordPress’s core nonce, which a full-
page cache serves stale or foreign. Refused writes are recorded and reported on 
a new Dashboard panel.

#### 1.3.2

Fixed three misplaced `phpcs:ignore` annotations from 1.3.1. Translation catalogues
regenerated.

#### 1.3.1

Packaging-only: rewrote three queries for static-analysis compliance, no behavioural
change.

#### 1.3.0

WordPress 6.2 is now the minimum, for the `%i` identifier placeholder. A write this
site cannot actually record is now refused with a 503 instead of reported as saved,
and the banner no longer treats a cache/firewall interstitial as a successful write.
The `www`/apex spellings of the site’s own host are both accepted by the same-origin
check. Thirteen Plugin Check errors closed.

#### 1.2.0

The architectural rewrite: cache-independent front end, a hash-chained HMAC-signed
consent ledger, genuine DSAR fulfilment via WordPress’s personal-data exporter/eraser
hooks, per-jurisdiction privacy notices and cookie policies (11 profiles), rate 
limiting on every public REST route, IAB TCF v2.2 and Google Consent Mode v2 support,
MaxMind GeoLite2 geo-routing, and a French translation. 349 defects closed against
the 1.1.0 baseline.

#### 1.1.0

Initial public release.

## Meta

 *  Version **2.0.8**
 *  Last updated **1 day ago**
 *  Active installations **Fewer than 10**
 *  WordPress version ** 6.2 or higher **
 *  Tested up to **7.1.3**
 *  PHP version ** 7.4 or higher **
 * Tags
 * [CCPA](https://wordpress.org/plugins/tags/ccpa/)[consent](https://wordpress.org/plugins/tags/consent/)
   [cookie banner](https://wordpress.org/plugins/tags/cookie-banner/)[GDPR](https://wordpress.org/plugins/tags/gdpr/)
   [privacy](https://wordpress.org/plugins/tags/privacy/)
 *  [Advanced View](https://wordpress.org/plugins/ow-consent/advanced/)

## Ratings

No reviews have been submitted yet.

[Your review](https://wordpress.org/support/plugin/ow-consent/reviews/#new-post)

[See all reviews](https://wordpress.org/support/plugin/ow-consent/reviews/)

## Contributors

 *   [ OptionWeb ](https://profiles.wordpress.org/optionweb/)

## Support

Got something to say? Need help?

 [View support forum](https://wordpress.org/support/plugin/ow-consent/)