Live · status OK
Documentatie · OW Consent v1.4.3

OW Consent
Documentatie

Toestemmingsbeheer voor WordPress dat trackers blokkeert vóór de klik, niet erna.

v1.4.3GPL-2.0-or-laterDocumentatie

OW Consent — Documentatie

Volledig toestemmingsbeheer in WordPress: banner voor meerdere rechtsgebieden, blokkering van trackers, scanner, juridische documenten, bewijsregister en rechtenportaal. Auteur: OptionWeb — Julien Daniel Plugin-pagina: https://optionweb.dev/nl/addons/ow-consent/ Licentie: GPL-2.0-or-later Versie die dit document beslaat: 1.4.3


Inhoudsopgave

  1. Overzicht
  2. Installatie
  3. Snelstart
  4. De elf compliance-profielen
  5. De cookiecategorieën
  6. De banner
  7. De automatische blokkering
  8. De trackerscanner
  9. De generator voor juridische documenten
  10. Het toestemmingsregister
  11. Het rechtenportaal (DSAR)
  12. De CCPA-opt-out “Do Not Sell or Share”
  13. Google Consent Mode v2
  14. IAB TCF v2.2
  15. Global Privacy Control
  16. De regiodetectie
  17. De OW Forms-integratie
  18. De zwevende knop
  19. Shortcodes
  20. REST API
  21. Instellingenreferentie
  22. Probleemoplossing
  23. FAQ

Overzicht

OW Consent is een suite voor toestemmingsbeheer in WordPress. Ze dekt de volledige keten: een keuze tonen, die keuze ook echt toepassen op de trackers, het bewijs ervan bewaren, de documenten publiceren die het uitleggen, en de verzoeken van betrokkenen ontvangen.

Er worden elf compliance-profielen meegeleverd — van de AVG tot de CCPA, met daartussen de Quebecse Wet 25, de Braziliaanse LGPD en de Indiase DPDP. Het actieve profiel bepaalt het toestemmingsmodel (opt-in of opt-out), de standaardwaarden van Google Consent Mode, de tekst van de gegenereerde documenten, de gepubliceerde rechten en de vermelde toezichthoudende autoriteit.

Alles blijft binnen uw site. De plugin neemt contact op met één enkele externe dienst, de Global Vendor List van IAB Europe, en alleen als u de TCF-module inschakelt, die standaard uit staat. Geen telemetrie, geen account, geen abonnement.

Verenigbaar met een volledige paginacache, by design

Dit is het architectuurpunt waar al de rest uit volgt. Niets van wat de server rendert hangt af van de toestemmingscookie. De HTML is voor alle bezoekers identiek: de trackers worden voor iedereen tot inerte tags herschreven, en het is een JavaScript-runtime die ze in de browser vrijgeeft, categorie per categorie, door de cookie te lezen vóór de eerste render.

Praktisch gevolg: LiteSpeed Cache, WP Rocket, Varnish of een CDN kunnen de keuzes van de ene bezoeker niet aan de andere serveren. Alleen de antwoorden die werkelijk persoonlijk zijn — de identiteitsbevestigingspagina van een rechtenverzoek, de REST-antwoorden die gegevens van de bezoeker bevatten — worden expliciet als niet-cachebaar gemarkeerd. Er wordt evenmin ooit een nonce in cachebare HTML afgedrukt: de banner haalt er vlak vóór elke schrijfactie een verse op via een no-store-endpoint.

Wat er wordt meegeleverd

  • Toestemmingsbanner: vier posities, licht / donker / automatisch thema, “Alles weigeren” even zichtbaar als “Alles accepteren”, een voorkeurenpaneel dat met het toetsenbord bedienbaar is, volledig vertaalbaar.
  • Automatische trackerblokkering: scripts, inline snippets, iframes, pixels, resource hints (preconnect, dns-prefetch, preload), analytics- en marketingstylesheets en media van derden, op basis van 175 signaturen die worden meegeleverd en aanpasbaar zijn.
  • Trackerscanner: een scan van uw eigen pagina's via WP-Cron, het uitlezen van Set-Cookie-headers, een browserprobe die aan beheerders is voorbehouden, en een paneel dat altijd vertelt wat de scan werkelijk heeft gedekt.
  • Generator voor juridische documenten: privacybeleid en cookiebeleid voor de elf profielen, in de taal van het rechtsgebied; wettelijke vermeldingen en algemene voorwaarden in het Frans.
  • Toestemmingsregister: elke actie wordt toegevoegd aan een via HMAC geketende tabel, met het geldende profiel, de hash van de gepubliceerde documenten en de hash van de banner die werkelijk is getoond.
  • Rechtenportaal (DSAR): een formulier via shortcode, verificatie per e-mail, de termijn van artikel 12.3 die start bij de identiteitsbevestiging, en een koppeling met de native exporter en eraser van WordPress.
  • Google Consent Mode v2, IAB TCF v2.2, Global Privacy Control, regiodetectie, OW Forms-integratie: elk afzonderlijk in te schakelen.
  • Een volledige REST API onder de namespace owc/v1.

Wat het niet doet — te lezen vóór u ermee in zee gaat

Bij een juridisch onderwerp stelt een te grote belofte u even hard bloot als de uitgever. Hieronder de grenzen, zoals ze in de code staan.

  • De gegenereerde documenten zijn modellen, geen juridisch advies. Elk document eindigt met een waarschuwing die dat zegt, en die waarschuwing staat standaard aan. Laat uw documenten nalezen voordat u ze publiceert.
  • De TCF-module is geen bij IAB Europe geregistreerde CMP. Ze vereist een CMP ID dat u zelf moet aanvragen, ze biedt geen enkele keuze op vendorniveau, en vendors mogen haar signaal terecht weigeren. Als advertentie-inkomsten onder TCF voor u meetellen, gebruik dan een gecertificeerde CMP.
  • Het register is maar onvervalsbaar onder voorwaarde. De keten is alleen een bewijs als haar ondertekeningssleutel buiten de database leeft. De plugin detecteert het tegendeel, rapporteert het zelf en zegt het u in de beheeromgeving in plaats van het omgekeerde te beweren.
  • De scanner voert geen JavaScript uit. Hij ziet wat uw HTML bevat en wat uw Set-Cookie-headers plaatsen; wat een tagmanager tijdens runtime injecteert, wordt alleen gezien door een probe die aan ingelogde beheerders is voorbehouden.
  • De standaardblokkering is niet “alles wat van derden komt”. Een onbekend script van derden wordt standaard toegelaten; het zijn de onbekende iframes die standaard worden geblokkeerd. De blokkering steunt op de signaturencatalogus, die u kunt aanvullen.
  • Zonder JavaScript kan er geen enkele keuze worden geregistreerd. De bezoeker ziet een <noscript>-blok en er wordt niets geladen dat niet strikt noodzakelijk is, maar er wordt ook niets geregistreerd.
  • Geen onderscheid per staat in de Verenigde Staten en evenmin per provincie in Canada bij de regiodetectie: het volledige Amerikaanse grondgebied krijgt het profiel ccpa, heel Canada krijgt quebec.
  • Wettelijke vermeldingen en algemene voorwaarden bestaan alleen in het Frans. Voor elk rechtsgebied waarvan de doeltaal niet het Frans is, weigert de generator die twee documenten te produceren in plaats van een ongeschikte tekst te publiceren.
  • Geen aparte rol: de volledige beheeromgeving vereist de capability manage_options.

Deze lijst is een bewuste keuze, geen verborgen roadmap. Bij compliance is een tool die zijn gaten benoemt beter dan een tool die ze verbergt.


Installatie

Vanuit de .zip

  1. Download ow-consent-1.4.3.zip van https://optionweb.dev/nl/addons/ow-consent/
  2. Plugins → Nieuwe plugin → Plugin uploaden
  3. Kies het bestand, klik op Nu installeren en daarna op Activeren

Via FTP

Pak het archief uit en zet de map ow-consent in /wp-content/plugins/, en activeer de plugin daarna via Plugins.

Vereisten

  • WordPress 6.2 of nieuwer — dat is een weigering om te starten, geen aanbeveling (zie hieronder)
  • PHP 7.4 of nieuwer
  • MySQL 5.7+ / MariaDB 10.2+
  • Een werkende WP-Cron als u de scanner, de bewaartermijn van het register, de DSAR-termijnherinneringen of de TCF-module gebruikt

Het vangnet WordPress 6.2. Sinds 1.3.0 wordt elke tabelnaam gebonden via de %i-placeholder van wpdb::prepare(), die WordPress pas vanaf 6.2 begrijpt. Op een oudere core zou prepare() een lege string teruggeven, zou de trackercatalogus stilletjes als een lege verzameling worden opgelost, en zou de banner de bezoeker een blokkering blijven beloven die niet plaatsvindt. De plugin weigert dus te starten en toont een foutmelding die expliciet zegt dat er niets wordt geblokkeerd en dat er geen enkele toestemming wordt geregistreerd. De header Requires at least verhindert de activatie onder 6.2 al, maar dekt noch een downgrade van de core onder een draaiende installatie, noch een kopie die via FTP is neergezet.

Wat er wordt geïnstalleerd

Bij activatie maakt OW Consent vier tabellen aan:

TabelInhoud
{prefix}owc_ledgerHet toestemmingsregister, geketend via hashes
{prefix}owc_dsarDe verzoeken van betrokkenen
{prefix}owc_scannerDe vondsten van de trackerscanner
{prefix}owc_scriptsDe signaturencatalogus die de blokkering gebruikt

Een vijfde tabel, {prefix}owc_form_links, wordt apart aangemaakt door de OW Forms-integratie als die zusterplugin actief is (zie De OW Forms-integratie).

De activatie voegt ook toe:

  • de optie owc_settings, leeg geseed — bewust: zolang u niets hebt opgeslagen, komen alle teksten uit de Engelse standaardwaarden, die on the fly naar de taal van de site worden vertaald, in plaats van de locale van wie de plugin activeerde in de database vast te zetten;
  • de optie owc_version;
  • de meegeleverde trackercatalogus (data/tracker-catalog.json), ingevoegd in blokken van 100 met INSERT IGNORE, met een seed-vlag per versie (owc_catalog_seeded_1.4.3);
  • de dagelijkse taak owc_daily_maintenance, ingepland één uur na de activatie.

Vijf andere cron-events worden opgezet door de modules die ze gebruiken. De zes cron-hooks van de plugin zijn: owc_scanner_run, owc_scanner_run_batch, owc_ledger_retention, owc_tcf_refresh_gvl, owc_run_upgrade en owc_daily_maintenance.

In multisite worden de tabellen per site aangemaakt, nooit gedeeld (wp_2_owc_ledger, enzovoort). Een netwerkactivatie loopt alle sites af alleen als het netwerk hoogstens 200 sites telt; daarboven wordt elke site lui geprovisioneerd bij haar eerste request. Een subsite die na een netwerkactivatie wordt aangemaakt, wordt geprovisioneerd via de hook wp_initialize_site.

Wat u in wp-config.php zet

Geen van deze constanten is verplicht, maar twee ervan veranderen de bewijskracht van wat de plugin produceert.

// Aanbevolen: haalt de ondertekeningssleutel van het register uit de database.
define( 'OWC_LEDGER_KEY', 'een lange willekeurige string, uniek voor deze site' );

// Aanbevolen: de standaard WordPress-salts. Zonder die salts slaat WordPress ze op in de
// database, en meldt het register zichzelf als niet-onvervalsbaar.
define( 'AUTH_KEY',  '…' );
define( 'AUTH_SALT', '…' );

// Als de site achter een CDN, een load balancer of een reverse proxy draait.
// Zonder deze constante worden CF-Connecting-IP, X-Forwarded-For en X-Real-IP GENEGEERD
// en wordt alleen REMOTE_ADDR gebruikt — waardoor al uw bezoekers dezelfde identiteit
// krijgen voor de rate limiting.
define( 'OWC_TRUSTED_PROXY', '198.51.100.0/24, 2001:db8::/32' );
// `OWC_TRUSTED_PROXIES` wordt als alias aanvaard; een string of een array kan allebei.

// Alleen voor de regiodetectie: verklaart welke landheaders betrouwbaar zijn.
define( 'OWC_GEO_TRUSTED_HEADERS', 'cloudflare' ); // 'cloudflare'|'cloudfront'|'proxy'|'all'
define( 'OWC_BEHIND_CLOUDFLARE', true );
define( 'OWC_BEHIND_CLOUDFRONT', true );

OWC_LEDGER_KEY dient ook om de sleutel van de regiodetectiecookie af te leiden. Is die constante niet gedefinieerd, dan valt de sleutel van het register terug op wp_salt('auth').

Wie toegang heeft

De volledige beheeromgeving van de plugin en alle REST-routes voor beheer vereisen de capability manage_options. Er bestaat geen aparte rol en geen fijnere capability: iemand het scherm van de plugin geven, komt neer op hem de instellingen van de site geven.

Het upgradepad

De plugin migreert haar schema nooit inline op een anonieme pagina. Wanneer de versie verandert, worden owc_version en een vlag owc_pending_upgrade meteen weggeschreven, en daarna:

  • draait de migratie inline als het request een beheerrequest buiten AJAX is, een cron-uitvoering of een WP-CLI-commando;
  • wordt anders vijf seconden later een event owc_run_upgrade ingepland.

admin-ajax.php wordt als een anoniem request behandeld: het is een publiek entrypoint. De migratie draait onder een lock (owc_upgrade_lock, na 300 seconden overneembaar), en een vangnet op admin_init vangt de sites op waar WP-Cron uit staat. Bij een identieke versie bedraagt de totale kost één enkele get_option().

Deactiveren en verwijderen

Deactiveren behoudt alle gegevens en wist enkel de zes cron-events. Bij een netwerkdeactivatie worden alle sites in batches van 200 afgelopen — anders dan bij de activatie — omdat een cron die actief blijft staan nooit meer zou verdwijnen.

Verwijderen van de plugin start uninstall.php, die in twee fasen werkt:

  1. Altijd, wat u ook hebt ingesteld: de zes crons worden gewist en de tabel {prefix}owc_dsar wordt verwijderd, samen met haar transients voor rate limiting. Dat is de enige tabel met direct identificerende persoonsgegevens van derden (e-mailadres, naam, vrije tekst); zodra de plugin weg is, begrenst niets nog de bewaring ervan en is er geen scherm meer om erop te antwoorden, ze te exporteren of ze te wissen. Exporteer de verzoeken die u moet bewaren vóór u de plugin verwijdert.
  2. Alleen als delete_data_on_uninstall uitdrukkelijk aan staat: verwijdering van de tabellen owc_ledger, owc_dsar, owc_scanner, owc_scripts en owc_form_links, van de benoemde opties, van alle opties met het voorvoegsel owc_ (transients inbegrepen) en van de usermeta met het voorvoegsel owc_. In multisite volgen de netwerkopties de beslissing van de hoofdsite.

Die instelling staat standaard uit: het toestemmingsbewijs, vereist door artikel 7.1 van de AVG, overleeft het verwijderen van de plugin.


Snelstart

Open na de activatie het menu-item OW Consent in de zijbalk van de beheeromgeving. De volledige plugin past op dat ene scherm, verdeeld over tien tabbladen: Dashboard, Banner, Compliance, Legal identity, Categories, Policies, Scanner, Tracker catalogue, Audit ledger, DSAR requests.

Opslaan geldt alleen voor het geopende tabblad. Dat is bewust: elke boolean heeft een gespiegeld verborgen veld, en een sleutel die niet in het formulier zit betekent “dit veld staat op een ander tabblad”, nooit “uitgevinkt”. Zonder dat zou één tabblad opslaan de instellingen van alle andere overschrijven.

1. Kies uw compliance-profiel

Tabblad Compliance. Het profiel bepaalt het toestemmingsmodel, de standaardwaarden van Consent Mode, de gegenereerde documenten en de gepubliceerde rechten. Standaard: gdpr.

compliance_strict staat standaard aan: dat is wat de blokkering verder doet reiken dan alleen de door WordPress geregistreerde scripts. Laat die instelling aan als u wilt dat de iframes, de pixels en de scripts die hard in uw thema staan ook worden behandeld.

2. Vul de juridische identiteit in

Tabblad Legal identity. Deze velden zijn de grondstof van de gegenereerde documenten, en het genereren wordt geweigerd zolang een verplicht veld leeg is — met de lijst van ontbrekende sleutels, niet met een stille mislukking.

Minimum voor alle documenten: legal_company_name en legal_company_email. Voor alles behalve het cookiebeleid komen daar het adres en het land bij. Voor de wettelijke vermeldingen komen ook het telefoonnummer, de verantwoordelijke uitgever en de volledige gegevens van de host erbij; in Frankrijk, België en Luxemburg worden ook de rechtsvorm en het ondernemingsnummer verplicht.

Laat legal_dpa_authority leeg: de toezichthoudende autoriteit wordt afgeleid uit uw land en uw profiel. Ze met de hand invullen op een site die meerdere rechtsgebieden bedient, komt erop neer dat u de verkeerde toezichthouder noemt.

3. Controleer uw categorieën

Tabblad Categories. De zes categorieën zijn standaard beschikbaar. Schakel uit wat uw site niet gebruikt: een categorie die uit de interface verdwijnt levert geen compliance op, het is een bron die voor altijd geblokkeerd blijft zonder schakelaar om ze vrij te geven — de blokkering verplaatst ze dan naar marketing.

Laat de labels leeg zolang ze u bevallen: ze volgen dan de taal van de site. Zodra u een tekst aanpast, houdt die tekst op de taal te volgen.

4. Stel de banner in

Tabblad Banner. Positie, thema, knoppen, labels, vernieuwingstermijn.

Twee dingen om niet te missen: laat banner_reject_all aan staan (“Weigeren” moet even eenvoudig en even zichtbaar zijn als “Accepteren”), en laat het sluitkruisje uit — het ontbreekt standaard omdat sluiten zonder keuze neerkomt op een impliciete weigering. Zet u het toch aan, dan voert een klik op het kruisje het volledige pad “Alles weigeren” uit, nooit een stille sluiting.

5. Genereer uw documenten

Tabblad Policies. Vier documenten: cookiebeleid, privacybeleid, wettelijke vermeldingen, algemene voorwaarden. Elk ervan wordt een WordPress-pagina met versiebeheer, waarvan de link terug in de instellingen wordt gezet.

Genereer eerst een preview, lees na, en publiceer daarna pas. En laat het nalezen door een professional: de waarschuwingsbalk onderaan elk document is geen decoratie.

6. Open het rechtenportaal

Maak een pagina aan en plak er [owc_dsar_form] in. Vul dsar_email in op het tabblad DSAR requests: dat is het contactadres dat onder het formulier wordt gepubliceerd en de ontvanger van de meldingen. Zonder dat adres valt de plugin terug op legal_dpo_email en vervolgens op admin_email — maar admin_email wordt nooit op een publieke pagina getoond.

7. Controleer voordat u opengaat voor het publiek

Het dashboard voert vijftien compliance-controles uit en maakt onderscheid tussen fouten en waarschuwingen: banner uitgeschakeld, “Alles weigeren” ontbreekt, sluitkruisje aan, onvolledige juridische identiteit, gesloten rechtenportaal, register uitgeschakeld, geen enkel gekoppeld document, toezichthoudende autoriteit in tegenspraak met het actieve profiel (met een knop “Fix this” die ze terugzet), site niet op HTTPS, niet-gecategoriseerde trackers, DSAR-verzoeken buiten termijn, catalogusregels die nooit kunnen afgaan.

Zet die vijftien regels op groen voordat u aankondigt dat uw site conform is.


De elf compliance-profielen

Het compliance-profiel is niet cosmetisch. Het stuurt het wettelijke model, de interface, de technische signalen en de inhoud van de gepubliceerde documenten.

De lijst

ProfielBeoogd regimeModel
gdprAVG + ePrivacy (EU/EER)Opt-in
uk_pecrUK GDPR + PECR (Verenigd Koninkrijk)Opt-in
ch_nfadpZwitserse nFADPOpt-in
quebecWet 25 (Quebec)Opt-in
lgpdLGPD (Brazilië)Opt-in
popiaPOPIA (Zuid-Afrika)Opt-in
piplPIPL (China)Opt-in
dpdpDPDP Act 2023 (India)Opt-in
ccpaCCPA / CPRA (Californië)Opt-out
us_genericAlgemene Amerikaanse staatswettenOpt-out
auPrivacy Act (Australië)Opt-out

Instelling: compliance_profile, standaard gdpr.

Opt-in betekent dat vóór elke keuze alleen de verplichte categorieën zijn toegestaan. Opt-out betekent dat alles is toegestaan tot aan de weigering. De drie opt-outprofielen zijn ccpa, us_generic en au. De JavaScript-runtime past exact dezelfde regel toe als PHP, zodat server en browser onmogelijk twee verschillende toestanden kunnen rapporteren.

Wat het profiel werkelijk verandert

Wat er verandertDetail
ToestemmingsmodelOpt-in, behalve ccpa, us_generic, au
Standaardwaarden van Consent ModeOnder een opt-outprofiel gaan de zeven signalen op granted
Verplichte link “Do Not Sell or Share”Alleen ccpa en us_generic
Juridisch bindende GPCAlleen ccpa en us_generic — Australië is uitdrukkelijk uitgesloten: opt-outregime, maar het erkent GPC niet
Bereik “de AVG is van toepassing” voor TCF27 EU-landen + IS, LI, NO + GB + CH, samen 31 codes
Taal van het gegenereerde documentquebec → Frans; lgpd → Portugees; gdpr met land FR, BE of LU → Frans; al de rest → Engels
Vermelde toezichthoudende autoriteitTabel per profiel, voor de AVG verfijnd per land
Gepubliceerde lijst van rechtenEen per profiel geschreven lijst, met artikelverwijzing
Blok “cookieregime” van het Engelse documentTekst, doorgiftebereik en waarborgen verschillen per profiel

De gegenereerde documenten per profiel

Het privacybeleid en het cookiebeleid bestaan voor de elf profielen. De sjablonen worden in deze volgorde opgelost, waarbij de eerste treffer wint: <type>_<profiel>_<taal>, dan <type>_<profiel>, dan <type>_<taal>, dan <type>.

De Engelse teksten van het cookiebeleid zijn profielbewust: de toepasselijke regel, het doorgiftebereik, de waarborgen en het opschrift van het paneel verschillen voor gdpr, uk_pecr, ch_nfadp, au, pipl, dpdp, ccpa en us_generic, met de bijhorende verwijzingen (art. 5(3) van richtlijn 2002/58, PECR reg. 6, art. 45c(b) TCA en art. 19/6(7)(b) nFADP, APP 8, art. 24 PIPL, secties 5/6/7/9(3)/16 van de DPDP Act 2023, §1798.121 en Cal. Code Regs. tit. 11 §7025, VCDPA/CPA/CTDPA/UCPA/TDPSA).

Vermelde toezichthoudende autoriteit

ProfielGenoemde autoriteit
gdpr, land FRCNIL
gdpr, land BEGBA-APD
gdpr, land LUCNPD
gdpr, land DEBfDI
gdpr, ander EER-landAlgemene formulering (“de bevoegde toezichthoudende autoriteit”)
uk_pecrICO
ch_nfadpEDÖB / FDPIC
quebecCommission d'accès à l'information
lgpdANPD
ccpaCalifornia Privacy Protection Agency
popiaInformation Regulator (South Africa)
piplCyberspace Administration of China
dpdpData Protection Board of India
auOAIC
us_genericDe Attorney General van uw staat

Hebt u legal_dpa_authority aangepast, dan heeft uw invoer voorrang op die afleiding.

Grenzen van de juridische dekking

  • Slechts vier EER-landen hebben een genoemde autoriteit (FR, BE, LU, DE). Een AVG-site in Spanje, Italië of Nederland publiceert een algemene formulering.
  • Geen onderscheid per staat in de Verenigde Staten. Het profiel us_generic bestaat en heeft zijn eigen teksten VCDPA/CPA/CTDPA/UCPA/TDPSA, maar geen enkele automatische detectie kent het toe: u kiest het met de hand.
  • Geen onderscheid per provincie in Canada.
  • Slechts drie documenttalen: Frans, Engels, Portugees. De teksten lopen niet via het vertaalmechanisme van WordPress — dat is bewust: een juridisch document hoort ééntalig te zijn en zijn taal volgt het rechtsgebied, nooit de locale van de beheerder.
  • Wettelijke vermeldingen en algemene voorwaarden: alleen Franse sjablonen. De generator weigert botweg voor elke andere doeltaal, tenzij u uw eigen tekst aanlevert via het filter owc_policy_template.

De cookiecategorieën

De canonieke lijst

Zes categorieën, in deze weergavevolgorde, met necessary altijd bovenaan:

SlugMeegeleverd labelMeegeleverde beschrijving
necessaryNecessaryStrictly required for the site to function (cart, login, language preferences). Cannot be disabled.
functionalFunctionalEnhance the experience (chat, embedded videos, maps). Without them some features may not work.
analyticsStatisticsHelp us understand how you use the site (anonymously). No personal data is shared for commercial purposes.
marketingMarketingEnable us to show you ads and content tailored to your interests on other sites.
preferencesPreferencesRemember your interface choices (layout, saved filters).
socialSocial & embedsAllow embedded social content (YouTube, Instagram, X) to load.

De waarden hierboven zijn de opslagbare Engelse standaardwaarden. Op een Nederlandstalige site ziet de bezoeker de vertaling — zie het vertaalmechanisme verderop.

Beschikbaarheid

Instelling cat_<slug>_available, één per categorie. Alle zes zijn standaard beschikbaar. De oude sleutel cat_<slug>_enabled wordt nog als terugval gelezen, zonder migratie.

preferences en social worden beschikbaar meegeleverd omdat de seed-catalogus YouTube, Spotify, SoundCloud, Instagram, X en Facebook in social onderbrengt: zonder die categorie in de interface zouden die integraties definitief geblokkeerd blijven zonder enige manier om opt-in te geven.

necessary is verplicht: ze kan niet worden uitgeschakeld (art. 5.3 ePrivacy). Ze heeft dus geen beschikbaarheidsschakelaar in de beheeromgeving, alleen een label en een beschrijving.

Het vertaalmechanisme

Instellingen cat_<slug>_label en cat_<slug>_desc. De opgeslagen waarde wordt alleen letterlijk teruggegeven als ze niet leeg is en verschilt van de Engelse standaardwaarde; anders komt de vertaling eruit.

Gevolg om te kennen: zolang u niets aanpast, verandert de taal van de categorieën mee met de taal van de site. Zodra u uw eigen tekst invoert, ligt die tekst vast in de taal waarin u hem hebt geschreven. Dezelfde regel geldt voor de teksten van de banner.

De vertaalde beschrijving van necessary wordt uit twee strings opgebouwd: de tweede noemt de cookies van de plugin zelf, owc_consent (tot 13 maanden) en owc_geo (24 uur, alleen geschreven wanneer de regiodetectie aan staat).

Koppeling met Google Consent Mode v2 — bindend

Dit is de enige bron van waarheid van de plugin: de banner, de blokkering en de bootstrap lezen allemaal deze tabel, zodat ze onmogelijk uit elkaar kunnen lopen.

CategorieSignalen van Consent Mode v2
necessarysecurity_storage, functionality_storage
functionalfunctionality_storage, personalization_storage
analyticsanalytics_storage
marketingad_storage, ad_user_data, ad_personalization
preferencespersonalization_storage
socialad_storage, ad_user_data

Belangrijke nuance: toont uw site de categorie functional, dan wordt functionality_storage weggehaald uit de lijst van necessary. Anders zou het worden toegestaan vóór elke toestemming, terwijl het bij een optionele categorie hoort.

Koppeling met TCF — enkel beschrijvend

functionalfunctional, analyticsmeasurement, marketingadvertising, preferencespersonalization, socialsocial_media.

Dat zijn weergavelabels. De bindende koppeling aan TCF-zijde is de tabel met de numerieke IAB-doel-ID's, beschreven in de sectie IAB TCF v2.2. Hang nooit TCF-gedrag op aan deze vijf labels.

Wat er gebeurt als een categorie verdwijnt

Wijst een catalogusregel naar een categorie die uw site niet meer toont — u hebt bijvoorbeeld social uitgeschakeld — dan verplaatst de blokkering de bron naar marketing, of anders naar de eerste beschikbare optionele categorie. Zonder die normalisatie zou de bron voor altijd geblokkeerd blijven zonder enige schakelaar om ze vrij te geven.


De banner

Wanneer hij verschijnt

De banner wordt gerenderd op wp_footer met prioriteit 5, en zijn assets worden in de wachtrij gezet op wp_enqueue_scripts. Hij stopt onmiddellijk als een van deze voorwaarden waar is: beheercontext, RSS-feed, robots.txt, of banner_enabled uitgeschakeld. Hij stopt ook als wp_head nooit is afgevuurd — een thema dat wp_head() niet aanroept zou anders inerte, ongestylede markup krijgen.

De HTML is voor alle bezoekers identiek. Alle vakjes van het paneel worden aan de serverzijde op OFF gerenderd en daarna aan de clientzijde uit de cookie gehydrateerd. Het is JavaScript dat <html data-owc="given|none"> synchroon plaatst, vóór de eerste render; de CSS toont de banner enkel bij data-owc="none".

Belangrijk gevolg: een bezoeker zonder JavaScript ziet de banner nooit, en zit dus ook nooit gevangen achter een modaal venster dat hij niet kan sluiten. In de plaats daarvan ziet hij een <noscript>-blok dat uitlegt dat zijn voorkeuren niet kunnen worden geregistreerd en dat er geen enkele niet-noodzakelijke cookie wordt geladen zolang er geen keuze is gemaakt.

Posities en thema

InstellingWaardenStandaard
banner_positionbottom-bar, bottom-card, center-modal, top-barbottom-bar
banner_styleauto, light, darkauto

Alleen center-modal krijgt role="dialog", aria-modal="true", een verduisterde achtergrond, een focus trap en de Escape-toets. De drie andere posities zijn een role="region": Escape wordt niet onderschept (het thema houdt zijn eigen afhandeling) en de focus wordt bij de eerste render niet weggenomen, wat een toetsenbordgebruiker voorbij alle skiplinks zou duwen.

Het donkere palet wordt toegepast onder prefers-color-scheme: dark, met veiligheden om een thema dat uitdrukkelijk light verklaart niet te overschrijven. Het thema van de site kan het palet sturen via CSS-variabelen: --owc-paper-tint, --owc-ink, --owc-smoke, --owc-fog, --owc-ui, --owc-accent, --owc-accent-strong, --owc-accent-darker.

Implementatiedetails: z-index 99998 voor de banner en 99997 voor de achtergrond, ondersteuning van env(safe-area-inset-*) voor de iOS-notch, en een automatische verschuiving onder de WordPress-adminbalk in de bovenste positie.

De knoppen

InstellingStandaardEffect
banner_accept_alltrueToont “Alles accepteren”
banner_reject_alltrueToont “Alles weigeren”
banner_preferencestrueToont “Aanpassen”
banner_close_xfalseToont het sluitkruisje
banner_show_logotrueToont het logo van de site

“Alles accepteren” en “Alles weigeren” delen dezelfde opmaakklasse: dezelfde achtergrond, dezelfde rand, dezelfde tekstdikte, dezelfde padding. Dat is het antwoord op de eis van CNIL en EDPB (richtsnoeren 03/2022): weigeren moet even eenvoudig en even zichtbaar zijn als accepteren.

Het sluitkruisje

Het staat standaard uit, omdat een sluitkruisje neerkomt op een impliciete weigering. Drie gedragingen om te kennen:

  1. Staat banner_close_x uit, dan zit het kruisje wel in de markup maar draagt het het attribuut hidden, en haalt de CSS het volledig weg: niet zichtbaar, niet focusbaar, niet aangekondigd aan schermlezers.
  2. Staat het aan, dan voert een klik op het kruisje het volledige pad “Alles weigeren” uit, nooit een stille sluiting.
  3. Het wordt in één enkel geval door de runtime getoond en van een ander label voorzien: wanneer een weigering helemaal niet kon worden geregistreerd. Het weigert dan niets meer, het bergt de melding op — en die sluiting telt niet als een keuze. Een bezoeker van wie de server de beslissing weigert, blijft niet achter met een banner die hij niet kan sluiten.

De eerste laag

  • De titel (text_title) en de boodschap (text_message). De boodschap is het enige veld met rijke HTML van de plugin; ze wordt gerenderd met een wp_kses die tot <a href target rel> beperkt is.
  • De link naar het privacybeleid: link_privacy_policy, met terugval op de beleidspagina die in WordPress zelf is aangeduid als de instelling leeg is.
  • De link naar het cookiebeleid: link_cookie_policy, zonder terugval.
  • De naam van de verwerkingsverantwoordelijke: legal_company_name, anders de naam van de site, weergegeven als “Verwerkingsverantwoordelijke: …” (art. 13(1)(a)).
  • Het logo: het aangepaste logo van het thema in formaat medium, anders het site-icoon.

Het voorkeurenpaneel

Eén regel per beschikbare categorie, binnen een benoemde role="group".

  • Een verplichte categorie toont een tekstbadge “Always active”, zonder schakelaar.
  • Een optionele categorie toont een <button role="switch"> met aria-checked, aria-labelledby en aria-describedby. De status wordt gedragen door aria-checked, de stand van de knop én een zichtbaar woord On/Off — nooit door kleur alleen.
  • Vergroot aanraakdoel van 44 px op kleine schermen (WCAG 2.2 AA, criterium 2.5.8).
  • De actiebalk van het paneel is sticky onderaan, zodat “Mijn keuzes opslaan” bereikbaar blijft terwijl de lijst scrollt.

Is de TCF-module actief, dan verschijnen er twee extra blokken: de bereikbare TCF-doelen en de opgegeven speciale functies. Zie IAB TCF v2.2.

Vernieuwing van de toestemming

InstellingStandaardGrenzen
consent_renewal_months120 tot 13
consent_policy_hash_checktrue

0 betekent niet “nooit opnieuw vragen”. De waarde 0 wordt omgezet naar 13 maanden, en 13 maanden is het harde plafond (CNIL-beslissing 2020-091). De effectieve duur ligt dus altijd tussen 1 en 13 maanden, en het is diezelfde waarde die de levensduur van de cookie stuurt, de vervalcontrole aan de serverzijde en de duur die in de gegenereerde documenten wordt gepubliceerd. De constante OWC_COOKIE_TTL die u in de broncode ziet, is enkel een terugval.

consent_policy_hash_check vraagt de toestemming opnieuw wanneer uw documenten wijzigen: de cookie draagt een hash van de gekoppelde beleidspagina's, en een verschil opent de banner opnieuw. Die hash is de lege string wanneer er geen enkele pagina gekoppeld is — dat is wat toelaat om “onbekend” van “gewijzigd” te onderscheiden en nooit een hele site opnieuw lastig te vallen op basis van een lege vergelijking.

De teksten

Zeven plaatsen, alle standaard leeg en dus automatisch vertaald: text_title, text_message, text_accept_all, text_reject_all, text_preferences, text_save, en floating_button_label voor de zwevende knop.

In de beheeromgeving toont elk veld zijn vertaalde standaardwaarde als placeholder: leeg laten behoudt de standaardwaarde die de taal van de bezoeker volgt.

Met het filter owc_banner_texts kunt u deze teksten via code vervangen. Extra plaatsen worden aanvaard; niet-scalaire of lege waarden worden weggelaten, zodat een onhandige callback de banner niet kan leegmaken.

Hoe de keuze werkelijk wordt geregistreerd

De JavaScript-runtime is strikt ES5 — geen arrow functions, geen template literals — om te werken in browsers die in apps zijn ingebouwd en in oude WebViews. Zijn schrijfvolgorde is het kennen waard, want die verklaart de meeste foutmeldingen.

  1. Het herankeren van de origin. De REST-URL's komen uit de WordPress-configuratie. Surfen uw bezoekers op een andere host (www tegenover apex, alias, previewdomein, een proxy die Host herschrijft), dan is die URL cross-origin en weigert de browser de Set-Cookie op te slaan terwijl WordPress netjes 200 antwoordt. De runtime verankert het pad dat PHP aanlevert daarom opnieuw op de origin die werkelijk wordt bezocht.
  2. Nonce. Vlak vóór de schrijfactie vers opgehaald via GET /owc/v1/nonce, nooit ingebakken in cachebare HTML, verstuurd in de header X-OWC-Nonce. Dat ophalen is afgetopt op 4 seconden, en een ontbrekende, lege of onbereikbare nonce verhindert nooit de schrijfactie en wordt evenmin aan de bezoeker gemeld.
  3. Expliciete page_url in de body van het request, zodat de registerregel niet afhangt van de header Referer, die een extensie, een meta-referrer of een proxy kan weghalen.
  4. Blokkeerbeveiliging van 15 seconden met AbortController: het request wordt afgebroken, niet alleen genegeerd, zodat een late POST geen tweede regel schrijft.
  5. Definitie van succes. De POST is alleen geslaagd als: het antwoord ok is, de body parseerbare JSON is, json.ok === true, en de cookie in de browser opnieuw kan worden gelezen. Een cachepagina, een edge challenge of een WAF die 200 antwoordt telt niet mee.
  6. Antwoordt de server cookie_set: false, dan schrijft de runtime de cookie zelf met de teruggegeven parameters en leest ze daarna opnieuw. Een mislukking blijft een mislukking: er wordt niets gepubliceerd, er wordt niets gedeblokkeerd.
  7. Eén enkele nieuwe poging, en alleen bij een 403 met de code owc_bad_nonce, rest_cookie_invalid_nonce of rest_nonce_invalid, en alleen als de verkregen nonce werkelijk verschilt.
  8. Wat de runtime publiceert, is wat de server heeft opgeslagen: de categorieën die de server heeft geweigerd, worden van de lokale status afgetrokken.

Zolang de POST niet is geslaagd, wordt er niets zichtbaar gemaakt, niets gedeblokkeerd en geen enkel Consent Mode-signaal “granted” uitgestuurd.

De fouttaxonomie

De runtime onderscheidt negen oorzaken, elk met een eigen zichtbare boodschap: network, refused, ratelimit, unexpected, timeout, browser, config, cookie, owc_cookie_not_persisted. De HTTP-status en de servercode worden op het element gezet (data-owc-status, data-owc-code) en één keer via console.warn gelogd — nooit als zichtbare tekst gerenderd. Een screenshot van de gebruiker benoemt dus de oorzaak, zonder dat er een netwerktrace nodig is.

De JavaScript-API van de banner

window.OWCBanner.show();              // opent in bannermodus
window.OWCBanner.hide();              // sluit
window.OWCBanner.openPreferences();   // opent het voorkeurenpaneel
window.OWCBanner.openDnsmpi();        // opent het paneel met marketing, social
                                      // en preferences al op OFF (CCPA-ingang)
window.OWCBanner.reset();             // wist de cookie aan de clientzijde, post een
                                      // intrekking en herlaadt de pagina
window.OWCBanner.acceptCategory( 'social' );  // geeft een promise terug die alleen op true
                                              // uitkomt als de server het echt opsloeg

Een lichtere bootstrap wordt in <head> afgedrukt met prioriteit 1 en stelt window.OWConsent beschikbaar: .config, .categories, .geo, .profile, .optOut, .state, .has(cat), .refresh(), .paint(), .gcmSignals(state). De configuratie ervan is filterbaar via owc_bootstrap_config — maar alles wat daarin staat is publiek en wordt door de cache gedeeld: zet er nooit gegevens in die eigen zijn aan één bezoeker.

Het paneel openen vanuit uw pagina's

Drie manieren, alle drie gelijkwaardig:

  • het URL-fragment #owc-preferences (of #owc-dnsmpi), dat bij het laden en bij hashchange wordt opgepikt;
  • elk element met de klasse owc-open-preferences of het attribuut data-owc-open;
  • het attribuut data-owc-dnsmpi="1" om het gedrag “Do Not Sell” af te dwingen.

Bij elke wijziging wordt een event owc:consent-changed uitgezonden, met de verstuurde payload in het detail. Het wordt beluisterd door de bootstrap, de zwevende knop, de CCPA-runtime en de TCF-module; u mag er ook naar luisteren.

Grenzen van de banner

  • Alles hangt af van wp_head() en wp_footer(): een thema dat ze niet aanroept, krijgt niets.
  • Zonder window.fetch en window.Promise is geen enkele schrijfactie mogelijk en wordt de fout browser getoond.
  • Een toestemming kan aan de serverzijde geregistreerd zijn zonder in de browser bewaard te blijven (siteadres verschilt van de bezochte host, opslag geblokkeerd, volle cookie jar). De runtime detecteert dat geval, weigert het als een succes te tellen, toont een specifieke boodschap en geeft het feit bij de volgende schrijfactie door aan de server.
  • De banner heeft geen shortcode: hij verschijnt overal of nergens.

De automatische blokkering

Dit is de module die van een keuze een reëel effect maakt. Ze herschrijft de tags die trackers dragen vóór elke toestemming, voor iedereen, en laat de runtime ze in de browser vrijgeven.

Twee modi, één instelling

compliance_strict, standaard aan.

  • Uit (soepele modus): alleen het filter script_loader_tag is aangehaakt. Met andere woorden, alleen de via wp_enqueue_script() geregistreerde scripts worden herschreven. Een <script> dat hard in het thema staat, een embed, een iframe, een <img>-pixel: daar wordt niets aan geraakt.
  • Aan (strikte modus): naast het filter vangt een output buffer het volledige document op, en wordt ook de markup die via de REST API wordt gerenderd behandeld. In de beheeromgeving dekt een aparte buffer bij een admin-ajax.php-request de antwoorden die ook de front-end bedienen (“meer laden”, gefilterde archieven) — maar alleen als de handler zelf Content-Type: text/html heeft opgegeven.

Wat nooit wordt gebufferd

De strikte buffer stopt onmiddellijk voor: de beheeromgeving, AJAX-requests, feeds, robots.txt, trackbacks, cron, favicons, REST-requests, JSON-requests, de preview van de customizer, sitemaps, en de entrypoints wp-login.php, wp-register.php, wp-signup.php, xmlrpc.php, wp-cron.php, wp-trackback.php — getest op de exacte scriptnaam, nooit als deelstring van de URL. Hij stopt ook voor de requests van de scanner, die de ruwe HTML nodig hebben.

De vijf passes

Het herschrijven gebeurt met gekalibreerde reguliere expressies, nooit met een DOM-parser. Drie vangnetten omkaderen het geheel: een document van meer dan 8 MB wordt onaangeroerd teruggegeven, een fout in de PCRE-engine geeft het document onaangeroerd terug, en is het resultaat kleiner dan de helft van de oorspronkelijke omvang, dan gaat het origineel terug. Met andere woorden: de faalmodus van de blokkering is “pagina geserveerd zonder blokkering”, nooit “kapotte pagina”.

Pass 0 — maskeren. HTML-commentaar, <style> en <textarea> worden vervangen door markers op basis van stuurtekens, zodat geen enkel later patroon binnenin kan matchen.

Pass 1 — <script>. De beslissing, in volgorde:

  1. al behandeld → onaangeroerd;
  2. script van de plugin zelf → onaangeroerd;
  3. type="text/plain" of een niet-JS-type (ld+json, importmap, x-template) → onaangeroerd;
  4. de URL wordt gezocht in src, data-src, data-rocket-src, data-lazy-src, data-litespeed-src, data-cfsrc — de attributen van de performanceplugins zijn dus gedekt;
  5. host op de allowlist → onaangeroerd; first-party bron die niet op een tracker lijkt → onaangeroerd;
  6. vergelijking met de catalogus op de vorm host + pad; een data:- of javascript:-URL wordt gedecodeerd en als inline code beoordeeld;
  7. een regel die als necessary is ingedeeld wordt nooit geblokkeerd (Stripe.js, reCAPTCHA, Turnstile, cdnjs …): ze blokkeren levert geen enkele compliance op en breekt het aanroepende snippet;
  8. geen enkele treffer → blocker_unknown_script_policy, standaard allow;
  9. anders herschrijven naar type="text/plain" met data-owc-cat, data-owc-vendor, data-owc-src en, als het oorspronkelijke type bijzonder was (module bijvoorbeeld), data-owc-type, om het herstelde script niet te degraderen.

Inline code wordt eerst vergeleken met de catalogusregels van het type inline_signature, en daarna met veertien vast ingebouwde, verankerde signaturen: fbq(, _fbq.push, gtag(, dataLayer.push(, ga('…, _gaq.push, _paq.push, hjid, clarity(, ttq.load|track|page, snaptr(, twq(, lintrk(, pintrk(. Uitdrukkelijke uitzondering: een losstaande gtag('consent', …), dus een Consent Mode-standaardverklaring zonder andere markers, blijft uitgevoerd worden.

Pass 2 — <iframe>. Dezelfde URL-attributen, plus data-original en data-srcset. First-party of allowlist → onaangeroerd. necessary → onaangeroerd, anders zou een formulier met captcha niet meer verstuurbaar zijn. Geen treffer → blocker_unknown_iframe_policy, standaard block. De geblokkeerde iframe krijgt src="about:blank" en wordt verpakt in een visuele vervanging met een knop “Accepteer die de bijhorende categorie deblokkeert.

Pass 3 — <img>, uitsluitend trackingpixels. Belangrijke regel: een catalogusregel alleen volstaat nooit om een afbeelding onschadelijk te maken. Komt de afbeelding overeen met de catalogus maar is haar categorie niet analytics of marketing — een afbeeldings-CDN, een gravatardienst, een lettertypehost — dan blijft ze onaangeroerd, anders zou u de media van de site verwijderen in plaats van een tracker. Alleen de tag-heuristiek kan blokkeren: elf bekende verzamelpunten (facebook.com/tr, px.ads.linkedin.com, ct.pinterest.com, bat.bing.com, analytics.twitter.com, t.co/i/adsct, tr.snapchat.com, analytics.tiktok.com, google-analytics.com/collect, googleads.g.doubleclick.net, pixel.quantserve.com) of een afbeelding van derden van 1×1 pixel. De src wordt dan vervangen door een transparante GIF, en alle attributen die een URL dragen worden verwijderd, zodat een lazy loader het origineel niet herstelt.

Pass 4 — <link>. Afhankelijk van blocker_block_resource_hints, standaard true. Behandelt enkel de hosts die al in de catalogus staan, nooit een onbekende.

  • Resource hints (preconnect, dns-prefetch, prefetch, prerender, preload, modulepreload) naar een gecatalogiseerde derde worden verwijderd, niet uitgesteld: een hint opent een TCP+TLS-verbinding en geeft het IP-adres en de TLS-vingerafdruk van de bezoeker prijs, en er valt achteraf niets te herstellen.
  • Een stylesheet die als analytics of marketing is ingedeeld, wordt onschadelijk gemaakt.
  • Google Fonts en de andere lettertypehosts die als functional zijn ingedeeld worden nooit aangeraakt: ze onschadelijk maken levert op de hele site tekst in een fallback-lettertype op, zonder enige compliancewinst.

Pass 5 — <object>, <embed>, <source>, <video>, <audio>. Nooit blokkeren bij gebrek aan gegevens: alleen een reeds gecatalogiseerde derde wordt behandeld. De attributen autoplay en preload worden verwijderd.

Wat als “first party” telt

De site-URL, de WordPress-URL, de content-URL, de includes-URL, hun netwerkvarianten in multisite, en de basis van de uploads. De www. wordt bij de vergelijking weggelaten. Elk pad dat met /wp-content/ of /wp-includes/ begint is first-party, ongeacht de host, om CDN-herschrijvingen te dekken. Niet-HTTP-schema's (data:, blob:, javascript:) zijn nooit first-party.

Tegenuitzondering: een URL die gtag, gtm.js, analytics, pixel, fbevents, hotjar, matomo, piwik of clarity bevat, wordt ook op de host van de site zelf behandeld. Dat is wat een zelfgehoste GTM of Matomo opvangt, en de first-party proxy's.

De runtime die deblokkeert

Afgedrukt in <head> met prioriteit 2. Zijn JSON-configuratie bevat enkel de naam van de cookie en de lijst van bewaakte hosts — niets dat van de bezoeker afhangt.

  • Status lezen: de cookie owc_consent wordt gedecodeerd tot ze stabiel is, met maximaal drie passes, omdat cookies die vóór 1.4.3 werden geschreven twee keer waren gecodeerd.
  • Bewaking van dynamisch geïnjecteerde scripts: de setter HTMLScriptElement.prototype.src en setAttribute worden ingepakt. Een first-party loader die s.src = 'https://www.googletagmanager.com/gtm.js?id=…' toekent, wordt onderschept en het element wordt vóór uitvoering gemarkeerd. De bewaakte lijst telt twintig hosts: googletagmanager.com, google-analytics.com, googleadservices.com, googlesyndication.com, doubleclick.net, connect.facebook.net, static.hotjar.com, script.hotjar.com, clarity.ms, cdn.matomo.cloud, analytics.tiktok.com, snap.licdn.com, sc-static.net, static.ads-twitter.com, bat.bing.com, s.pinimg.com, cdn.segment.com, js.hs-scripts.com, cdn.amplitude.com, cdn.mxpnl.com. Alles wat er niet in staat, gaat ongemoeid door. De hosts van uw allowlist worden vóór het afdrukken uit die lijst gehaald.
  • Herinjectie in documentvolgorde. Een hersteld extern script blokkeert de wachtrij tot zijn onload of onerror, met een maximum van 5 seconden zodat een onbereikbare leverancier de rest niet ophoudt. Het attribuut async wordt alleen toegepast als het oorspronkelijk aanwezig was — zonder dat zou een script dat via createElement wordt gemaakt asynchroon worden en zou het configuratiesnippet vóór zijn bibliotheek draaien. Voor een hersteld inline script wordt document.write tijdelijk omgeleid om het document niet te wissen.
  • Intrekking van toestemming: gaat een categorie die al was toegepast uitdrukkelijk naar false, dan herlaadt de runtime de pagina. Een script dat al is uitgevoerd, kan niet worden uitgeladen (art. 7.3 AVG). Een categorie die simpelweg ontbreekt in de payload is geen intrekking.
  • Een MutationObserver scant markup die na het laden wordt geïnjecteerd opnieuw (AJAX, lui geladen secties).
  • Een klik op de knop van een geblokkeerde embed roept OWCBanner.acceptCategory() aan; bestaat de API van de banner niet, dan wordt de embed lokaal gedeblokkeerd, zonder iets op te slaan.

De signaturencatalogus

Tabel {prefix}owc_scripts. Het meegeleverde bestand, data/tracker-catalog.json, bevat 175 regels die 71 verschillende leveranciers dekken:

VerdelingDetail
Per doel110 URL-patronen, 65 cookienamen
Per type172 tekstfragmenten, 3 reguliere expressies
Per categorienecessary 55, marketing 37, functional 37, analytics 36, social 10

Kolommen: pattern, pattern_type (host, regex, inline_signature), match_target (url of cookie), name, vendor, category, privacy_policy_url, gcm_signal, tcf_vendor_id, retention_days.

Twee gedragingen om te kennen:

  • De regels met match_target = 'cookie' worden nooit door de blokkering gebruikt. Ze dienen enkel voor de cookietabel van de gegenereerde documenten. Ze als deelstring van een URL vergelijken is precies wat het JavaScript van sites kapotmaakte.
  • Een patroon van het type host dat op een URL mikt, korter dan zes tekens en zonder punt, wordt geweigerd: “fr” of “IDE” zouden “frame.js” en “provider.js” matchen.

De catalogus wordt gecachet in de objectcache (1 uur) en in een transient (12 uur). Een leeg resultaat wordt nooit gecachet. Elke schrijfactie vuurt de actie owc_catalog_updated af, die deze caches leegt.

Op het tabblad Tracker catalogue kunt u regels toevoegen, wijzigen, verwijderen en doorzoeken, met een filter “alleen de regels die nooit kunnen afgaan”: leeg of onzichtbaar patroon, reguliere expressie die niet compileert of catastrofaal terugkeert, patroon dat te kort is en geen punt bevat voor een URL, categorie die niet meer bestaat. Die diagnose gebeurt in PHP, omdat alleen de PCRE-engine kan zeggen of een expressie compileert.

Instellingen van de blokkering

InstellingStandaardWaarden
compliance_stricttrueboolean
blocker_unknown_script_policyallowallow, block
blocker_unknown_iframe_policyblockallow, block
blocker_block_resource_hintstrueboolean
blocker_allowlist''één host per regel

Uitbreidingspunten

// Hosts die nooit worden geblokkeerd, bovenop blocker_allowlist.
add_filter( 'owc_blocker_allowlist', function ( array $hosts ) {
    $hosts[] = 'cdn.mijn-partner.example';
    return $hosts;
} );

// Catalogusregels vóór de validatie.
add_filter( 'owc_scripts_catalog', function ( array $rows ) {
    $rows[] = array(
        'pattern'      => 'tracker.example.com',
        'pattern_type' => 'host',
        'match_target' => 'url',
        'name'         => 'Voorbeeld',
        'vendor'       => 'Voorbeeld NV',
        'category'     => 'analytics',
    );
    return $rows;
} );

De publieke methode OWC_Blocker::block_html_fragment( $html ) past de blokkering toe op een fragment dat niet door de output buffer is gegaan.

Grenzen van de blokkering

  1. Zonder compliance_strict worden alleen de door WordPress in de wachtrij gezette scripts behandeld.
  2. Een onbekend script van derden wordt standaard toegelaten. De echte blokkering steunt op de catalogus en de inline signaturen — vul ze aan.
  3. De bewaking van scripts die door JavaScript worden geïnjecteerd, dekt maar twintig hosts; een tracker buiten die lijst die door first-party code wordt geïnjecteerd, glipt erdoor.
  4. Stylesheets die als functional zijn ingedeeld (webfonts) worden bewust doorgelaten.
  5. Een tracker die als <img> in de catalogus staat maar anders dan analytics of marketing is ingedeeld, wordt niet onschadelijk gemaakt.
  6. Media (object, embed, source, video, audio) worden nooit standaard geblokkeerd: alleen een reeds gecatalogiseerde derde wordt behandeld.
  7. Een document van meer dan 8 MB, een PCRE-fout of een verlies van meer dan 50 % van de inhoud geeft een pagina die zonder enige blokkering wordt geserveerd, stilletjes.
  8. Het intrekken van de toestemming veroorzaakt een volledige herlading van de pagina.

De trackerscanner

De scanner inventariseert wat uw pagina's werkelijk laden. Hij staat standaard uit: zet hem aan op het tabblad Scanner.

Drie detectiebronnen

Elke vondst houdt haar herkomst bij, en ook de informatie “kan de blokkering hier iets tegen doen”.

1. De geserveerde HTML. De scanner haalt een steekproef van de URL's van uw eigen site op en leest de markup: <script src> en <script data-owc-src> — de al geblokkeerde scripts worden dus toch gezien —, inline signaturen, <iframe>, externe stylesheets en afbeeldingen waarvan de URL 1x1, pixel, track, beacon of impression bevat. Er wordt geen enkel stukje JavaScript uitgevoerd: wat een tagmanager tijdens runtime injecteert, is onzichtbaar voor deze pass.

2. De Set-Cookie-headers. Dat zijn de enige cookies die een serverscan kan bewijzen, inclusief de HttpOnly-cookies die een browserprobe nooit zal zien. Ze worden als niet blokkeerbaar gemarkeerd: niets in de clientblokkering kan een cookie tegenhouden die de server uitstuurt. De cookies van de plugin zelf worden genegeerd.

3. De browserprobe. Wordt in de footer afgedrukt alleen voor een ingelogde gebruiker met manage_options, en alleen als de scanner aan staat. Ze maakt bij het laden een foto van document.cookie, observeert de mutaties van de DOM — dat is wat opvangt wat een tagmanager injecteert —, bevraagt document.cookie elke 5 seconden voor cookies die aan de clientzijde worden geschreven, en stuurt haar waarnemingen elke 2 seconden door en bij beforeunload. Het antwoord dat de probe draagt, wordt als niet-cachebaar gemarkeerd.

In de code erkende grens: een beheerder heeft doorgaans al alles geaccepteerd. De probe beschrijft dus de toestand na de toestemming, niet die ervoor.

De scanronde

De wachtrij met URL's is opgebouwd om templates te dekken, niet pagina's: eerst de kern-URL's (homepage, statische voorpagina, berichtenpagina en de vier gekoppelde beleidspagina's), en daarna een afwisseling per familie — tot 30 pagina's, 15 berichten, 5 per publiek custom posttype, 3 categorieën en 3 tags. Zonder die afwisseling slokten dertig bijna identieke pagina's het hele budget op en werden de WooCommerce-templates nooit bereikt.

Met het filter owc_scanner_urls kunt u URL's toevoegen, maar het resultaat wordt opnieuw tot de host van de site beperkt: de scanner verlaat nooit uw domein.

Elk request gebeurt met redirects die met de hand worden gevolgd (maximaal twee hops, elke hop opnieuw getoetst aan de host van de site), een antwoord afgetopt op 2 MB, een header Cache-Control: no-cache, no-store en een unieke URL-parameter om geen cachepagina te lezen, en een user-agent OW-Consent-Scanner/<versie>. Een niet-HTML-antwoord telt als overgeslagen, niet als mislukt.

Er wordt een bypass-secret meegestuurd in de header X-OWC-Scanner zodat de blokkering zich terugtrekt en de ruwe HTML laat zien. Het wordt in constante tijd vergeleken, en de header wordt eerst getest zodat een gewone bezoeker zelfs het lezen van de optie niet uitlokt.

Budget, lock, hervatting

BeperkingWaarde
URL's per uitvoeringscanner_max_urls, standaard 25, grenzen 1 tot 500
Time-out per requestscanner_timeout, standaard 8 s, grenzen 1 tot 60
Kloktijdbudget per batchmax_execution_time − 10 s, anders 45 s, begrensd tussen 5 en 60 s
Uitvoeringslock15 minuten
Levensduur van de wachtrij6 uur

Raakt het budget op, dan wordt een minuut later een hervatting ingepland en gaat de scan verder waar hij gestopt was. Een tweede start terwijl er al een scan loopt, antwoordt “er loopt al een scan”. Kortsluiting: mislukken drie requests zonder dat er ook maar één pagina is gelezen, dan stopt de scan in plaats van zijn time-out te verbranden op tweeëntwintig extra URL's — het typische geval van een site waarvan de HTTP-loopback is geblokkeerd.

De herkende inline signaturen

Drieëntwintig zoekstrings, gegroepeerd in eenentwintig labels: gtag('config', gtag('event', gtag('js', gtm.start, ga('create', ga('send', fbq('init', fbq('track', _paq.push, (h.hj=h.hj, clarity('init', clarity.ms/tag, mixpanel.init, amplitude.init, amplitude.getInstance, _linkedin_partner_id, snaptr('init', ttq.load, pintrk("load", pintrk('load', rdt('init', criteo_q.push, _etmc.push.

De sleutel voor ontdubbeling is de signatuur, nooit een hash van de code: een echt gtag- of Pixel-snippet bevat waarden die eigen zijn aan de pagina, wat één regel per pagina zou opleveren.

De terugkoppeling: vondst → catalogus → blokkering

Dit is wat de scanner nuttig maakt. Een tracker indelen schrijft een regel in de blokkeringscatalogus. Een klik op de categorie van een vondst doet drie dingen: hij schrijft de categorie op de regel, hij onthoudt uw handmatige beslissing, en hij maakt de bijhorende regel aan. Aan het einde van elke volledige scan gebeurt dezelfde bewerking in bulk voor alles wat de catalogus nog niet kan indelen.

Conversieregels:

  • een vondst van het type cookie wordt een regel match_target = cookie op de naam van de cookie;
  • een vondst van het type inline_script schrijft alleen een regel als de oorspronkelijke zoekstring wordt teruggevonden. Zonder die string wordt er geen regel geschreven: een regel die uit het label “Google gtag (config)” zou zijn opgebouwd, zou voor altijd dood zijn én de naam van de vondst matchen, waardoor die voorgoed als “gedekt” zou doorgaan;
  • anders een host-regel op het domein, geweigerd als het domein leeg is.

Uw handmatige beslissingen worden apart bewaard (maximaal 500) en bij elke nieuwe waarneming opnieuw toegepast, omdat de vlag “bevestigd” ook “een catalogusregel heeft gematcht” betekent en dus niet in haar eentje de informatie “de beheerder heeft beslist” kan dragen. Terugzetten naar “niet gecategoriseerd” verwijdert de entry: dat is een echte stap terug.

De catalogus wordt vergeleken met het langste patroon eerst, zodat een brede regel (google-analytics.com) de preciezere regel die u hebt geschreven (www.google-analytics.com) niet overschrijft.

Planning, waarschuwingen, bewaring

scanner_frequency aanvaardt hourly, twicedaily, daily en weekly — maar de beheeromgeving biedt alleen de intervallen aan die uw installatie werkelijk kent. Standaard: weekly. De scanner uitschakelen haalt beide events uit de planning.

Twee verschillende e-mails, nooit allebei tegelijk:

  1. Niet-gecategoriseerde trackers — alleen verstuurd voor werkelijk nieuwe identificatoren, met een geheugen dat op 500 entries is afgetopt.
  2. “De scan kon deze site niet lezen” — beperkt tot één bericht per week en per foutsignatuur. Dat is de ernstigste faalmodus, want een scan die niets leest, produceert geen enkele vondst en dus ook geen waarschuwing van het eerste type.

Ontvanger: scanner_alert_email, anders het beheeradres van de site.

Bewaring: 90 dagen. Vondsten die al 90 dagen niet meer zijn waargenomen worden verwijderd, zodra er minstens één pagina is gelezen — niet alleen wanneer een scan volledig afloopt.

Het dekkingspaneel

Dat is het belangrijkste deel van het tabblad Scanner, en het voedt ook het waarschuwingskader van de gegenereerde documenten. Het beantwoordt altijd deze vragen:

  • is er al ooit een scan gestart?
  • is de laatste afgelopen, of is hij door zijn tijdsbudget onderbroken?
  • hoeveel pagina's zijn er werkelijk opgehaald (niet: uit de wachtrij gehaald)?
  • hoeveel konden dat niet, en wat was de eerste fout?
  • heeft een browser al ooit iets gerapporteerd, of is er nooit JavaScript waargenomen?
  • na welke termijn verdwijnt een tracker die niet meer is waargenomen?

Een scan die geen enkele pagina heeft bereikt, wordt gepresenteerd als een mislukking, niet als een net resultaat. Dit is het paneel dat u moet lezen vóór u een cookiebeleid publiceert dat op deze vondsten is gebouwd.

Het tabblad Scanner

Drie kaarten (gedetecteerde trackers, niet gecategoriseerd, laatste scan en volgende uitvoering), het dekkingspaneel, een knop “nu een scan starten”, het planningsformulier, en daarna de lijst met vondsten: filters per categorie, tekstzoekfunctie, filter per elementtype (script, inline_script, iframe, stylesheet, pixel, cookie, link, preconnect), filter per status, CSV-export, paginering per 25. Indelen per stuk of in bulk — de bulkkeuze heeft een leeg, uitgeschakeld eerste item, zodat een per ongeluk verstuurd formulier geen algemene herindeling wordt.

Instellingen van de scanner

InstellingStandaardGrenzen
scanner_enabledfalseboolean
scanner_frequencyweeklyhourly, twicedaily, daily, weekly
scanner_max_urls251 tot 500
scanner_timeout81 tot 60
scanner_probe_modeadminsadmins, off
scanner_alert_email''e-mailadres

Goed om te weten: scanner_probe_mode wordt in deze versie niet gebruikt. De code vermeldt dat uitdrukkelijk — de voorwaarde om de probe af te drukken leest deze instelling niet meer en steunt enkel op scanner_enabled. off opslaan bewaart de waarde zonder de probe uit te schakelen. Om de probe echt te stoppen, schakelt u de scanner uit.

Grenzen van de scanner

  1. De serverscan voert geen enkel stukje JavaScript uit. Zonder de probe ontbreekt in de inventaris wat een tagmanager injecteert.
  2. De probe is voorbehouden aan ingelogde beheerders en er bestaat geen enkele modus die haar voor een gewone bezoeker uitvoert: dat zou een eigen voorafgaande informatieplicht vereisen.
  3. Een cookie die via een Set-Cookie-header wordt geplaatst, wordt gedetecteerd maar is niet blokkeerbaar.
  4. De scan verlaat nooit het domein en is standaard op 25 URL's afgetopt: een grote site wordt nooit volledig gedekt.
  5. Op een host waar de HTTP-loopback is geblokkeerd (HTTP-authenticatie op een staging-omgeving, firewall) leest de scan niets.
  6. Een vondst die 90 dagen niet meer is waargenomen verdwijnt, en verdwijnt dus ook uit het cookiebeleid.
  7. De scanner hangt af van WP-Cron: op een site met DISABLE_WP_CRON en zonder systeemcron gaat de geplande scan niet af.

De generator voor juridische documenten

De vier documenten

TypeInhoudBeschikbare talen
cookie_policyCookiebeleidFrans, Engels, Portugees
privacy_policyPrivacybeleidFrans, Engels, Portugees
legal_noticeWettelijke vermeldingenalleen Frans
termsAlgemene voorwaardenalleen Frans

Elk gegenereerd document is een WordPress-pagina, van een versie voorzien via metadata: _owc_policy_type, _owc_policy_version (bij elke generatie opgehoogd), _owc_policy_hash (SHA-256-hash van de HTML), _owc_policy_generated_at, _owc_policy_profile, _owc_policy_lang, _owc_policy_manual_edit.

Na de generatie wordt de link teruggezet in de bijhorende instellingen (link_cookie_policy, link_privacy_policy, link_legal_notice, link_terms), en het publiceren van een privacybeleid werkt de beleidspagina bij die in WordPress zelf is aangeduid.

Twee sloten vóór de publicatie

Slot 1 — de verplichte velden. De generatie wordt geweigerd met de lijst van lege sleutels, in plaats van clausules met gaten te publiceren.

DocumentVereiste velden
Allelegal_company_name, legal_company_email
Behalve het cookiebeleid+ legal_company_address, legal_country
Wettelijke vermeldingen+ legal_company_phone, legal_publication_director, legal_host_name, legal_host_address, legal_host_phone
Wettelijke vermeldingen, land FR / BE / LU+ legal_company_legal_form, legal_company_reg_number

Slot 2 — de taal. De generator weigert een document te publiceren dat is opgesteld in een taal die het rechtsgebied niet gebruikt. In preview komt het document eruit met een rode waarschuwingsbalk; bij publicatie is het een botte weigering. Op het tabblad Policies wordt de generatieknop verborgen wanneer er geen sjabloon bestaat voor het actieve type en profiel, in plaats van te verschijnen en systematisch te falen.

Hoe de taal wordt bepaald

ProfielTaal van het document
quebecFrans
lgpdPortugees
gdpr met legal_country ∈ {FR, BE, LU}Frans
Al de restEngels

De verplichte waarschuwing

Zolang policy_disclaimer aan staat — en dat is standaard zo — eindigt elk document met een blok dat zegt dat het om een automatisch gegenereerd model gaat, dat het vóór publicatie door een gekwalificeerde professional moet worden nagelezen, en dat de generatiedatum en de versie van de plugin draagt. Zet die instelling alleen met kennis van zaken uit.

De trackertabel — de eerlijkheidsregels

De tabel die in het cookiebeleid wordt gepubliceerd, komt uit uw eigen scannertabel, niet uit een externe databank. Vijf regels bepalen wat ze toont.

  1. Versheidsvenster: alleen de vondsten die in de laatste 90 dagen zijn waargenomen worden gepubliceerd. Bestaat de kolom van de laatste waarneming nog niet omdat een migratie niet is doorgelopen, dan wordt het venster genegeerd in plaats van “geen enkele tracker” te publiceren op een site die er wel heeft — te weinig openbaar maken is de enige richting waarin een juridisch document nooit mag falen.
  2. Waarschuwingskader bovenaan de tabel, afgeleid van het dekkingspaneel van de scanner: scan nooit gestart, geen enkele pagina gelezen, scan onderbroken, N URL's mislukt, geen enkele waarneming met JavaScript actief, venster van N dagen. Een onvolledige scan wordt bekendgemaakt, niet als een afgeronde inventaris gepubliceerd.
  3. Niet-ingedeelde trackers worden niet verstopt: ze krijgen hun eigen sectie. Dat zijn de trackers die niemand heeft bekeken.
  4. Een cookie die in een Set-Cookie-header is waargenomen krijgt een eigen markering, met een noot die uitlegt dat hij door de server wordt geplaatst en dat geen enkele clientblokkering hem kan tegenhouden. Hij mag dus niet worden voorgesteld als afhankelijk van de toestemming, en de clausule die stelt dat niet-ingedeelde trackers pas na toestemming worden geplaatst, draagt de bijhorende uitzondering.
  5. De naam van de verantwoordelijke en de link naar diens beleid komen uit de catalogus, alleen voor de regels die op een URL mikken. Patronen op cookienamen zijn uitgesloten: in een juridisch document beweert u niet zonder bewijs wie gegevens verwerkt.

De cookies van de site en de gepubliceerde bewaartermijnen

De sectie “cookies die deze site plaatst” somt de cookies van de plugin zelf op: owc_consent (duur afgeleid van de vernieuwingsinstelling), euconsent-v2 als TCF aan staat, owc_geo als de regiodetectie aan staat, owc_gpc (sessieduur), owc_gpc_notice (5 minuten), plus de login- en instellingencookies van WordPress.

De tabel met bewaartermijnen haalt haar cijfers uit de werkelijke instellingenledger_retention_days, retention_form_data_days, retention_dsar_days en de vernieuwingstermijn van de toestemming. Er wordt geen enkele decoratieve waarde gepubliceerd. De levensduur van de toestemmingscookie wordt afgeleid uit dezelfde berekening als de cookie zelf, wat garandeert dat een instelling van 0 maanden wel degelijk “13 maanden” publiceert en niet “nooit”.

Detectie van het soort activiteit (algemene voorwaarden)

De algemene voorwaarden tellen 21 secties, met een schakelaar verkoper/dienstverlener, producten/diensten, offerte/bestelling, en clausules die specifiek zijn per soort activiteit.

legal_business_type (auto, vitrine, rental, ecommerce, services, saas, content) is de bron van waarheid. Staat die op auto, dan onderzoekt een heuristiek de site — aanwezigheid van WooCommerce, open registratie, abonnementsplugins, een prijzenpagina, dienstenpagina's, en daarna de trefwoorddichtheid over de laatste vijftig gepubliceerde items — maar haar oordeel wordt nooit afgedrukt in een gepubliceerd document: het dient enkel om de optionele clausules te kiezen.

Verwijzingen naar Franse wetsartikelen worden alleen ingevoegd als legal_country op FR staat. België heeft zijn eigen verwijzingen (WER art. VI.45 §1, art. VI.47, de Consumentenombudsdienst); Luxemburg en Quebec krijgen een neutrale formulering. Een niet geverifieerde tekst citeren zou het gebrek zijn, niet het geneesmiddel.

Ander gedrag

  • De datum van het document wordt in de locale van het document opgemaakt. Is het bijhorende vertaalpakket niet geïnstalleerd, dan valt het formaat terug op dd/mm/jjjj: een Frans document kan niet openen met “4 September 2026”.
  • Handmatige bewerking gedetecteerd: komt de opgeslagen inhoud niet meer overeen met haar hash, dan wordt er vóór het overschrijven een revisie bewaard en toont het scherm het label “handmatige wijzigingen vervangen”, met een link naar de revisies.
  • De generator degradeert nooit: een gepubliceerde pagina blijft gepubliceerd, ook als het vakje “publiceren” niet is aangevinkt, en een titel of permalink die u hebt hernoemd overleeft een nieuwe generatie.
  • Injectie in het footermenu: auto_footer_menu_inject, standaard uit. De plugin wijzigt uw publieke site niet zonder een uitdrukkelijke vraag. Herkende locaties wanneer u ze inschakelt: footer, footer-menu, footer_menu, footer-1, footer_1, secondary, legal.

Uitbreidingspunten

// De ruwe tekst van het sjabloon, met de {{variabelen}} nog op hun plaats.
add_filter( 'owc_policy_template', function ( $html, $type, $profile ) {
    return $html;
}, 10, 3 );

// De variabelen die in het sjabloon worden geïnjecteerd.
add_filter( 'owc_policy_vars', function ( array $vars, $type, $profile ) {
    return $vars;
}, 10, 3 );

// De uiteindelijke HTML, met de variabelen ingevuld.
add_filter( 'owc_policy_html', function ( $html, array $vars ) {
    return $html;
}, 10, 2 );

Uw eigen tekst aanleveren via owc_policy_template schakelt het taalslot uit: een site die haar eigen tekst levert, neemt de taal ervan voor haar rekening. Dat is de officiële weg om niet-Franse wettelijke vermeldingen te publiceren.

Vier vooraf opgebouwde blokken zijn als gereserveerde variabelen beschikbaar: {{__trackers_table__}}, {{__categories_list__}}, {{__retention_table__}}, {{__jurisdictional_rights__}}. Alle variabelen zijn al ge-escaped in de context van hun gebruik. Drie publieke methoden zijn herbruikbaar door een extern sjabloon: OWC_Policies::build_cookie_table(), build_data_retention_table() en render_first_party_cookies().

REST-routes

MethodePadParametersToegang
POST/owc/v1/policies/generatetype (vereist), publish (boolean, standaard false)manage_options
GET/owc/v1/policies/previewtype (vereist)manage_options

De HTML van de preview gaat door wp_kses_post() vóór ze wordt teruggegeven.

Grenzen van de generator

  1. Dit zijn modellen, geen juridisch advies. Laat ze nalezen.
  2. Wettelijke vermeldingen en algemene voorwaarden bestaan alleen in het Frans. Elk ander rechtsgebied botst op het taalslot, tenzij u uw eigen tekst aanlevert.
  3. Slechts drie talen: Frans, Engels, Portugees.
  4. Vier EER-landen hebben een genoemde autoriteit; elders een algemene formulering.
  5. De trackertabel is niet meer waard dan uw scan — en het document zegt dat ook.
  6. De klasse van de generator weegt ongeveer 440 KB aan juridische sjablonen: ze wordt lui geladen, uitsluitend wanneer een van de drie entrypoints werkelijk wordt aangesproken.

Het toestemmingsregister

Het register is het antwoord op artikel 7.1 van de AVG: kunnen aantonen dat de persoon heeft toegestemd. Het staat standaard aan.

De tabel

{prefix}owc_ledger, alle datums in UTC:

KolomTypeInhoud
idbigintPrimaire sleutel
created_atdatetimeUTC-tijdstempel
visitor_tokenchar(32)Pseudoniem token van de browser, 32 hexadecimale tekens
eventvarchar(20)accept_all, reject_all, save_preferences, withdraw, auto, gpc_opt_out
categoriesvarchar(255)Lijst van de toegestane categorieën
profilevarchar(20)Compliance-profiel dat gold op het moment van de actie
sourcevarchar(60)banner, preferences, footer_link, api, auto
ip_pseudonymousvarchar(45)Ingekort IP-adres
ua_hashchar(64)Gesalte hash van de user-agent
page_urlvarchar(500)Pagina waar de actie plaatsvond
prev_hashvarchar(128)Hash van de vorige regel
row_hashvarchar(128)Hash van deze regel
policies_hashchar(64)Hash van de geldende documenten
banner_revisionvarchar(40)Hash van de banner die werkelijk is getoond
plugin_versionvarchar(20)Versie van de plugin op het moment van schrijven

De laatste drie kolommen laten toe te reconstrueren wat de bezoeker heeft gezien, niet alleen wat hij heeft aangevinkt.

De keten

Elke regel wordt met HMAC ondertekend over een canonieke, met lengtes voorafgegane serialisatie van haar inhoud en van de hash van de vorige regel. De genesis is een reeks van 64 nullen. Het schrijven gebeurt in een transactie, met een rijvergrendeling op de laatste entry: het is die vergrendeling die gelijktijdige schrijfacties werkelijk serialiseert en verhindert dat de keten zich vertakt. Een benoemde MySQL-lock wordt als tweede bolwerk genomen, best effort: wordt die geweigerd, dan gaat het toevoegen door en wordt er een actie afgevuurd zodat u het kunt traceren.

De waarden worden vóór de ondertekening ingekort, zodat de ondertekende waarde exact de opgeslagen waarde is.

De eerlijkheid over de onvervalsbaarheid

De plugin bepaalt zelf waar haar ondertekeningssleutel vandaan komt:

HerkomstVoorwaardeOordeel
constantOWC_LEDGER_KEY is gedefinieerdOnvervalsbaar
wp-configAUTH_KEY en AUTH_SALT zijn gedefinieerd, niet leeg, onderling verschillend en zonder de standaardzinOnvervalsbaar
databaseAndersNiet onvervalsbaar

In het derde geval slaat WordPress de salts op in de database: wie toegang heeft tot die database, kan de keten opnieuw ondertekenen. De plugin rapporteert dat in het verificatieresultaat en toont een adminmelding die het met zoveel woorden zegt. Er wordt een onomkeerbare hash van de sleutel bewaard, waardoor een rotatie van de salts van een herschrijving kan worden onderscheiden.

Dat is de reden waarom de sectie Installatie aanraadt om OWC_LEDGER_KEY te definiëren.

De verificatie

De knop “Verify the chain now” op het tabblad Audit ledger — of de route GET /owc/v1/ledger/verify — doorloopt de volledige keten in batches van 500 regels.

Er worden drie ondertekeningsschema's herkend: het huidige canonieke schema, een ouder historisch schema, en een sleutelloos schema uit de allereerste versies. Dat laatste produceert een hash die iedereen met de database opnieuw kan berekenen: het wordt nooit als geldig beschouwd, het wordt apart geteld en als een breuk gemeld. Een sleutelloze hash is geen bewijs.

Gerapporteerde breuken: hash_mismatch, chain_break, bad_genesis, unkeyed_rows, table_emptied, tail_truncated, head_mismatch, count_mismatch.

Het resultaat bevat onder meer: ok, total, checked, table_total, broken_at, breaks, break_count, partial, legacy_rows, unverifiable_rows, anchor, anchor_ok, key_source, tamper_evident, key_rotated.

Het kopanker

Er wordt een anker buiten de tabel bewaard: identificator, hash, aantal regels, tijdstempel. Zonder dat anker zou het verwijderen van de recentste regels of het leegmaken van de tabel geen enkel spoor nalaten. Bij elke verplaatsing van het anker wordt een actie afgevuurd, en de code nodigt uitdrukkelijk uit om het buiten de database te repliceren — bestand, syslog, extern endpoint — zodat een volledige replay detecteerbaar wordt.

De plugin weigert een bestaand anker te overschrijven en meldt eerlijk dat een anker dat uit de tabel zelf is afgeleid, alleen latere afkappingen detecteerbaar maakt.

De bewaartermijn

ledger_retention_days, standaard 1825 dagen (5 jaar), grenzen 0 tot 3650. De waarde 0 betekent onbeperkte bewaring.

Het opschonen draait op zijn eigen dagelijkse cron en verwijdert alleen een aaneengesloten begin: nooit een gat midden in de keten. De hash van de laatst verwijderde regel wordt onthouden zodat de rest verifieerbaar blijft. Plafond per uitvoering: 20 000 regels (40 batches van 500). Een zeer grote achterstand wordt dus over meerdere dagen weggewerkt.

Gegevensminimalisatie

  • IP-adres: IPv4 met de laatste octet op nul (de /24 blijft behouden), IPv6 ingekort tot /48 met de resterende 80 bits op nul. Het maskeren gebeurt op de binaire vorm; een IPv6-adres dat een IPv4-adres mapt, wordt als IPv4 behandeld.
  • User-agent: alleen een gesalte hash wordt opgeslagen, nooit de string.
  • page_url: gevalideerd tegen de hosts van de site. De aanroeper heeft het laatste woord — levert hij de waarde, ook een lege, dan is dat het antwoord; de Referer wordt alleen geraadpleegd als hij helemaal niets heeft gezegd, en wordt op dezelfde manier gevalideerd.
  • Bezoekerstoken: 32 hexadecimale tekens uit een cryptografische generator, zonder enige band met een identiteit.

Het algoritme

ledger_hash_algo aanvaardt sha256 (standaard) en sha3-256, doorsneden met de algoritmen die uw PHP werkelijk ondersteunt. De instelling wordt in de beheeromgeving getoond maar is niet wijzigbaar: van algoritme veranderen zou de verificatie van alle bestaande regels doen mislukken. De kolommen zijn ruimer bemeten, maar de echte keuze blijft beperkt tot die twee algoritmen.

Het tabblad Audit ledger

Lijst met paginering per 25: identificator, lokale datum én ruw UTC-tijdstempel, bezoekerstoken, event, categorieën, profiel, gepseudonimiseerd IP-adres, en de schakel van de keten (vorige hash → huidige hash, in de weergave ingekort, de volledige waarde in een tooltip).

Filters: bezoekerstoken (32 hexadecimale tekens), event, profiel, datumbereik — ingevoerd in de tijdzone van de site en vergeleken met de UTC-tijdstempels die werkelijk zijn opgeslagen.

Export in CSV en JSON. De JSON krijgt een envelop mee (formaat, algoritme, herkomst van de sleutel) zodat een autoriteit het uittreksel opnieuw kan verifiëren zonder over de rest van de keten te beschikken. Alle CSV-cellen zijn beveiligd tegen injectie van spreadsheetformules.

Het register via de API raadplegen

GET /owc/v1/ledger aanvaardt page, per_page (1 tot 200, standaard 50), visitor_token, from en to. Met die laatste drie beantwoordt u een inzageverzoek (art. 15) zonder de hele keten te doorlopen. De grenzen worden in UTC gelezen, en een datum in het formaat JJJJ-MM-DD wordt tot de volledige dag verruimd. De filters die werkelijk zijn toegepast, worden in het antwoord teruggegeven: een filter dat door de controles is geweigerd, mag niet gelezen worden als “dit is het volledige register”.

Wat er gebeurt als het register een schrijfactie weigert

Dit is het belangrijkste gedrag van de hele plugin. Staat het register aan en mislukt het schrijven van de regel, dan wordt de toestemmingscookie ingetrokken en is het antwoord een 503. Er wordt niets opgeslagen en er wordt geen enkele tracker vrijgegeven.

De redenering is rechttoe rechtaan: een toestemming die u niet kunt bewijzen, mag u niet claimen. Zie Probleemoplossing voor de te volgen stappen.

Uitbreidingspunten

add_action( 'owc_consent_updated',           function ( $categories, $event, $source ) {}, 10, 3 );
add_action( 'owc_consent_cookie_not_sent',   function ( $cookie, $event ) {}, 10, 2 );
add_action( 'owc_ledger_lock_failed',        function ( $event, $token ) {}, 10, 2 );
add_action( 'owc_ledger_write_failed',       function ( $error, $event, $token ) {}, 10, 3 );
add_action( 'owc_ledger_anchor',             function ( array $anchor ) {} );
add_action( 'owc_ledger_pruned',             function ( $deleted, $days ) {}, 10, 2 );

owc_ledger_anchor is de hook om het anker buiten de database te repliceren.

Grenzen van het register

  1. De onvervalsbaarheid is voorwaardelijk, en de plugin zegt dat zelf.
  2. De regels van vóór 1.2.0 dragen een sleutelloze hash: ze worden nooit opnieuw ondertekend — opnieuw ondertekenen zou een aanvaller een vervalste geschiedenis laten laten ondertekenen — en zolang ze bestaan, verhinderen ze het oordeel “keten intact”.
  3. Het opschonen is afgetopt op 20 000 regels per dagelijkse uitvoering.
  4. Het register wordt niet verwijderd door de eraser voor persoonsgegevens van WordPress: het is een hashketen, ze weghalen zou het bewijs vernietigen waarvoor ze bestaat, en ze bevat enkel een pseudoniem token en een ingekort IP-adres. Een bericht legt die keuze uit aan de betrokkene.

Het rechtenportaal (DSAR)

Via het portaal kan iemand zijn rechten uitoefenen vanaf een pagina van uw site. Het staat standaard aan, maar het verschijnt alleen daar waar u de shortcode plaatst.

Het formulier

[owc_dsar_form]

Attributen:

AttribuutStandaardRol
typesaccess,rectification,erasure,portability,restrict,object,optoutAangeboden types, gescheiden door komma's
title“Mijn rechten op mijn persoonsgegevens uitoefenen”Titel van het blok
submit_label“Mijn verzoek versturen”Label van de knop

Staat dsar_enabled uit, dan toont de shortcode “het portaal staat uit” en antwoordt de REST-route met 404: het uitschakelen gebeurt wel degelijk aan de serverzijde.

De lijst die u opvraagt wordt doorsneden met de lijst die de server aanvaardt: een optie die het endpoint weigert, wordt de bezoeker nooit aangeboden.

De acht soorten verzoeken

access, rectification, erasure, portability, restrict, object, optout, withdraw.

withdraw is bewust weggelaten uit wat het formulier aanbiedt. Uw cookietoestemming intrekken gaat onmiddellijk via het voorkeurenpaneel; dat door een schriftelijke procedure van 30 dagen laten lopen, zou het intrekken moeilijker maken dan het toestemmen (art. 7.3 AVG). Een link onder het formulier opent voor dat geval rechtstreeks het voorkeurenpaneel.

Het filter owc_dsar_types is de enige bron van waarheid: de allowlist van het endpoint wordt eruit afgeleid.

De volledige cyclus

1. Indienen. POST /owc/v1/dsar. Drie serverbeveiligingen, in deze volgorde:

  • Honeypot: een ingevuld verborgen veld leidt tot een 400-afwijzing met de generieke boodschap van een misvormde inzending — een bot leert niets over de reden van zijn weigering. Aan de browserzijde toont het formulier zelfs de succesboodschap zonder iets te versturen.
  • Verklaring (art. 12.6): het vakje “ik bevestig dat ik een recht uitoefen op MIJN eigen persoonsgegevens” moet zijn aangevinkt, en dat wordt aan de serverzijde gecontroleerd, niet alleen in de browser. Het wordt samen met het verzoek bewaard.
  • Minimumtermijn van 3 seconden tussen het tonen van het formulier en het versturen, aan de clientzijde.

Rate limits: 3 per uur en per IP-adres, 3 per dag en per beoogd e-mailadres — dat adres kiest de aanvaller zelf, dus dat is wat moet worden afgetopt — en 30 per uur voor de hele site.

De regel wordt ingevoegd met de status pending, een token van 64 tekens waarvan alleen de SHA-256-hash wordt opgeslagen, een vervaldatum voor het token en een antwoordtermijn. De identificator wordt niet teruggegeven: geen enumeratie, geen lek over de volumes. Het antwoord is {ok, mail_sent, message}, en mail_sent weerspiegelt een werkelijke verzendfout.

De DPO wordt in dit stadium niet verwittigd. Anders zou een anonieme aanroeper met elke inzending twee e-mails vanaf uw domein doen vertrekken.

2. Verificatiemail. De link wijst naar uw homepage met het token in klare tekst als parameter. Geldigheid: dsar_token_ttl_days, standaard 7 dagen, grenzen 1 tot 90. De e-mail draagt een Reply-To-header maar nooit een herschreven From — de envelop herschrijven is wat SPF breekt. Het antwoordadres is dsar_email, anders legal_dpo_email; nooit het beheeradres van de site, dat niet gepubliceerd hoort te worden.

3. Identiteitsbevestiging. De link opent een pagina die niets uitvoert: de bevestiging gebeurt via een POST die door een nonce is beschermd. Dat is wat verhindert dat een linkscanner van een mailsysteem (Safe Links, URL Defense, inboxpreview) een identiteit bevestigt in de plaats van de persoon.

De pagina is een op zichzelf staand HTML-document, geserveerd buiten het thema, met noindex, nofollow en niet-cachebare headers. De pogingen zijn beperkt tot 30 per uur en per IP. De antwoorden zijn onderscheiden: 404 voor een onbekende link, 200 voor een al bevestigde link, 410 voor een verlopen link, 403 voor een verlopen nonce, 500 voor een schrijffout, 200 voor een bevestiging.

4. Wat de bevestiging in gang zet.

  • De status gaat naar verified.
  • De termijn wordt herberekend vanaf de verificatie. Artikel 12.3: de termijn loopt vanaf het moment dat het verzoek volledig is, niet vanaf een indiening die nooit is bevestigd.
  • Het token wordt verbrand. De hash ervan wordt bewust bewaard, zodat iemand die zijn link opnieuw opent “al bevestigd” leest in plaats van een 404 die hem vraagt alles opnieuw te doen. Het eenmalige gebruik wordt gegarandeerd door de statuscontrole.
  • Er worden bewaard: de bevestigingsdatum, het ingekorte IP-adres, het pseudonieme token van de browser — de enige mogelijke brug tussen het toestemmingsregister en het verzoek — en de identificator van het bijhorende WordPress-verzoek.
  • Er wordt een native WordPress-verzoek geopend met de identiteit al bewezen, dus meteen in uw wachtrij onder Gereedschap → Persoonsgegevens exporteren / wissen. Daardoor dekt de uitvoering alle plugins van de site, niet alleen OW Consent. Koppeling: access en portability → export; erasure → verwijdering; de andere types openen geen native verzoek.
  • De verantwoordelijke wordt verwittigd op dsar_email en dsar_notify_email, met terugval op het beheeradres als geen van beide geldig is.

5. Uitvoering. Een paneel boven het tabblad DSAR requests toont de 20 geverifieerde verzoeken, gesorteerd op termijn, met het aantal resterende dagen of dagen achterstand.

ActieGedrag
Gegevens downloaden (JSON)Weigering met 409 als de identiteit nooit is bevestigd. Het bestand heet dsar-<id>-<JJJJMMDD>.json
Gegevens wissenAlleen aangeboden voor een verzoek van het type erasure, en pas na de identiteitsbevestiging. Het verzoek wordt vóór de wissing afgesloten, anders zou de vrije tekst van het lopende verzoek zijn eigen uitvoering overleven
+2 maandenVerlenging uit art. 12.3, slechts één keer, met een motivering van maximaal 500 tekens. De termijn schuift 60 dagen op, de herinneringen worden opnieuw gezet, en de betrokkene krijgt een e-mail met de nieuwe termijn en de redenen
AfsluitenStatus resolved of rejected, notitie verplicht. Een al afgesloten verzoek kan niet worden herschreven, en “ingewilligd” noteren op een nooit bevestigde identiteit is onmogelijk
De link opnieuw versturenGeeft een nieuw token uit, waardoor het vorige vervalt

Vijf statussen: pending, verified, resolved, rejected, expired.

Het scherm laadt nooit het token of de hash ervan, en toont de naam van de aanvrager niet.

6. Bewaking van de termijn. Op de dagelijkse cron: tot 200 geverifieerde verzoeken doorlopen op termijn, een herinnering zeven dagen vooraf, een herinnering bij overschrijding, elk slechts één keer. Een balk in de beheeromgeving meldt de verzoeken buiten termijn en die met minder dan zeven dagen te gaan.

7. Opruimen. Inzendingen die nooit zijn geverifieerd worden verwijderd nadat hun token is vervallen, en afgesloten verzoeken worden verwijderd na retention_dsar_days. Die termijn begint bij het afsluiten, niet bij het indienen.

De portabiliteitsbundel

De export levert een document in het formaat ow-consent/dsar-export, met het tijdstempel, de site, de betrokkene, het verzoek en de gegevensgroepen. Ze roept alle geregistreerde exporters van de site aan, pagineert tot 50 pagina's per exporter en stopt na 20 seconden. De wissing doet hetzelfde aan de kant van de erasers, met dezelfde grenzen.

Filter: owc_dsar_export_bundle( $bundle, $email, $id ).

Wat de export en de wissing dekken hangt dus af van de plugins die op uw site zijn geïnstalleerd: een plugin die geen van deze hooks registreert, moet u met de hand behandelen.

Integratie met de native tools van WordPress

  • Exporter geregistreerd onder de sleutel ow-consent, met twee groepen: de rechtenverzoeken en het toestemmingsregister.
  • Eraser:
    1. inzendingen die nooit zijn bevestigd worden verwijderd — geen enkele bewijswaarde, alleen persoonsgegevens;
    2. bij afgesloten verzoeken worden de vrije tekst en de naam gewist, terwijl de minimale regel (datum, type, status) als bewijs bewaard blijft (art. 5.2) tot aan retention_dsar_days;
    3. een verzoek dat nog openstaat wordt bewaard, met een bericht dat uitlegt dat het moet worden beantwoord vóór het mag worden verwijderd;
    4. het toestemmingsregister wordt niet verwijderd, om de hierboven uitgelegde reden.
  • De plugin voedt ook het concept-privacybeleid van WordPress.

De informatie onder het formulier

Onder het formulier bevat een uitklapbaar blok de informatie uit artikel 13: identiteit en adres van de verwerkingsverantwoordelijke, privacycontact, doel en rechtsgrond (art. 6.1.c), verzamelde gegevens, ontvangers en bewaartermijn, antwoordtermijn en mogelijkheid tot verlenging, bevoegde toezichthoudende autoriteit, link naar het beleid. Filter: owc_dsar_form_notice( $html, $context ).

De getoonde autoriteit komt uit de afleiding die is beschreven in De elf compliance-profielen, en wordt alleen behouden als ze met een hoofdletter begint: de ruwe instelling uitlezen zou de Franse autoriteit noemen op een Zuid-Afrikaanse, Indiase, Australische of Californische site.

Zonder JavaScript wordt de verzendknop verborgen en stelt een bericht het contactadres voor — zodat de browser nooit een native inzending doet die het adres van de aanvrager in de URL zou zetten, en dus in de logs van alle doorkruiste servers en proxy's.

DSAR-instellingen

InstellingStandaardGrenzen
dsar_enabledtrueboolean
dsar_email''publiek contact en ontvanger van de meldingen
dsar_notify_email''bijkomende ontvanger
dsar_response_days301 tot 30 — nooit meer dan een maand
dsar_token_ttl_days71 tot 90
retention_dsar_days1095 (3 jaar)1 tot 3650

Uitbreidingspunten

add_action( 'owc_dsar_submitted',   function ( $type, $email, $message ) {}, 10, 3 );
add_action( 'owc_dsar_verified',    function ( $id, array $row ) {}, 10, 2 );
add_action( 'owc_dsar_fulfilled',   function ( $id, $what, $trace ) {}, 10, 3 );
add_action( 'owc_dsar_mail_failed', function ( $id, $email, $kind ) {}, 10, 3 );

Grenzen van het portaal

  1. De identiteit steunt op één enkele factor: de heen-en-terug per e-mail. Elk bijkomend bewijs op grond van art. 12.6 voert u met de hand in bij de notities van het verzoek.
  2. Een mislukte e-mailverzending blokkeert de cyclus: het antwoord toont mail_sent: false en het formulier toont een bericht dat naar het contactadres verwijst, maar het verzoek blijft pending en de wettelijke termijn start niet.
  3. De termijnherinneringen en het opruimen hangen af van WP-Cron.
  4. De tabel met verzoeken wordt bij het verwijderen van de plugin altijd verwijderd, wat u ook hebt ingesteld. Exporteer vóór u verwijdert.

De CCPA-opt-out “Do Not Sell or Share”

Onder de Amerikaanse profielen eist de wet een besturingselement dat benoemd is, zichtbaar is, en de opt-out ook werkelijk uitvoert.

Twee manieren om het te plaatsen

Shortcode, waar u maar wilt:

[owc_dnsmpi]
[owc_dnsmpi label="Verkoop of deel mijn persoonsgegevens niet" class="mijn-link"]
AttribuutStandaard
label“Do Not Sell or Share My Personal Information”
classowc-dnsmpi

Automatische injectie in de footer: instelling ccpa_inject_footer, standaard aan, gerenderd op wp_footer met prioriteit 20.

Wat een klik doet

Eén klik voert de opt-out uit. Hij opent geen paneel. De runtime haalt een verse nonce op en stuurt daarna een reject_all met de bron footer_link en alle optionele categorieën op false. De link gaat op aria-busy="true" en een zone met role="status" toont de stand van zaken: “bezig met registreren”, daarna “uw opt-out is voor deze browser geregistreerd”, of de foutmelding.

De code vermeldt de reden: volgens de uitvoeringsregels van de CCPA (§7026(a)(1)) is een link die enkel een paneel opent geen conform mechanisme.

Terugval zonder fetch en Promise (oude WebView, ingebouwde browser): de klik opent het voorkeurenpaneel met de reclamecategorieën al op off gezet.

De cachebeperking

De geserveerde HTML is voor iedereen identiek. De link wordt dus zichtbaar gerenderd als het geconfigureerde profiel van de site ccpa of us_generic is; anders wordt hij verborgen gerenderd en aan de clientzijde getoond voor de bezoekers wier rechtsgebied dat vereist, op basis van de regiodetectiecookie.

Staat de regiodetectie uit en is uw geconfigureerde profiel niet Amerikaans, dan wordt het blok gewoon niet afgedrukt: geen dode markup.

Er reist geen enkel bezoekersgegeven mee in de JavaScript-configuratie van deze module — enkel de lijsten met profielen, nooit de opgeloste boolean.

De assets van deze module worden in <head> afgedrukt en niet in de footer, zodat de Consent Mode-weigering gtag bereikt vóór een tagmanager afgaat. Het JavaScript is uitsluitend ES5.

Grenzen

  1. Het besturingselement met één klik vereist fetch en Promise.
  2. De link is alleen verplicht onder de profielen ccpa en us_generic; onder elk ander profiel wordt hij niet getoond en niet zichtbaar gemaakt.
  3. Het zichtbaar maken aan de clientzijde hangt af van de regiodetectiecookie, en dus van JavaScript.

Google Consent Mode v2

De module staat standaard aan (gcm_enabled).

De zeven signalen

De zeven signalen van Consent Mode v2 worden uitgestuurd: ad_storage, ad_user_data, ad_personalization, analytics_storage, functionality_storage, personalization_storage en security_storage. De zes categorieën van de plugin worden eraan gekoppeld vanuit één enkele bron van waarheid, wat garandeert dat de banner, de blokkering en de bootstrap niet uit elkaar kunnen lopen. De tabel staat in De cookiecategorieën.

De standaardwaarden, vóór elke toestemming

De default-aanroep wordt als statische markup afgedrukt, en de update wordt in de browser berekend: dat is wat de pagina cachebaar houdt.

SignaalOpt-inregimeOpt-outregime
ad_storagedeniedgranted
analytics_storagedeniedgranted
ad_user_datadeniedgranted
ad_personalizationdeniedgranted
personalization_storagedeniedgranted
functionality_storagedenied als de site de categorie functional toont, anders grantedgranted
security_storagealtijd grantedgranted

Beide sets worden in dezelfde pagina afgedrukt. De runtime kiest welke hij toepast op basis van de regiodetectiecookie. Zonder dat zou een gecachete pagina het rechtsgebied van een Amerikaanse bezoeker vastpinnen op een Europese bezoeker.

De twee bijhorende instellingen

InstellingStandaardEffect
gcm_ads_data_redactiontrueMaskeert de advertentie-identificatoren zolang ad_storage geweigerd is
gcm_url_passthroughtrueLaat gclid / dclid via de URL's passeren zolang de cookies geweigerd zijn

Goed om te weten

security_storage wordt nooit door een update aangeraakt: het blijft toegestaan, zoals de specificatie voorschrijft. Het dashboard toont een kaart die aangeeft of Consent Mode actief is.


IAB TCF v2.2

De TCF-module staat standaard uit, en ze vereist een identificator die de plugin niet kan leveren. Lees de sectie met de grenzen vóór u ze inschakelt.

Inschakelen

InstellingStandaardGrenzen
tcf_enabledfalseboolean
tcf_cmp_id00 tot 4095
tcf_publisher_countryFRISO-landcode van 2 letters
tcf_publisher_purposes_li[]lijst van doelen
tcf_special_features[]lijst van speciale functies

Het plafond van 4095 is niet willekeurig: het veld CmpId neemt 12 bits in de TC-string in.

Drie gevallen waarin de module weigert iets uit te sturen

  1. Geen CMP ID (tcf_cmp_id < 1): geen __tcfapi, geen TC-string, geen cookie euconsent-v2, geen REST-route. Een adminmelding legt dat uit. Het CMP ID moet u door IAB Europe worden toegekend; de plugin levert er geen. Een string uitsturen met CmpId 0 zou erger zijn dan niets uitsturen.
  2. CMP ID groter dan 4095: dezelfde weigering, met een eigen melding. Een waarde die op 12 bits wordt afgekapt, zou een andere CMP aanduiden — identiteitsdiefstal.
  3. Geen Global Vendor List in cache: de stub wordt afgedrukt maar de API antwoordt cmpStatus: 'error' met een lege string, in plaats van een versienummer van de lijst te verzinnen.

Er wordt een diagnose in een optie weggeschreven (disabled, missing_cmp_id, no_gvl, active), en uitsluitend in een beheer- of croncontext, nooit op een publieke pagina.

De Global Vendor List

  • Bron: https://vendor-list.consensu.org/v3/vendor-list.json.
  • Wordt nooit tijdens het renderen van een pagina gedownload. Een dagelijkse cron doet dat, met een eerste uitvoering vijf minuten na de activatie; in de beheeromgeving met een koude cache wordt één enkele ophaalactie in de wachtrij gezet.
  • Request: maximaal 5 seconden time-out, 2 redirects, antwoord afgetopt op 4 MB. Een body die het plafond bereikt, wordt als afgekapt beschouwd en geweigerd.
  • Alleen de nuttige velden per vendor worden bewaard; de stacks worden geleegd. Blijft de serialisatie te groot, dan gelden twee afkapniveaus: eerst de labels en de URL's, daarna de reductie tot enkel de doelen.
  • Bewaring: één week in een transient, met stale-if-error — bij een mislukking blijft de laatste geldige kopie bewaard.
  • Publieke route: GET /owc/v1/tcf/gvl, beperkt tot 10 requests per uur. Ze serveert de gecachete kopie met een ETag en een Cache-Control: public, max-age=86400, en behandelt conditionele requests. Zit er niets in de cache, dan antwoordt ze 503 met een Retry-After: 300 — nooit een verzonnen lijst. Deze route lokt nooit een uitgaand request uit.

Waar de TC-string wordt berekend

In de browser, niet in PHP. Een volledig statische stub wordt in <head> afgedrukt met prioriteit 0: locator-iframe, implementatie van window.__tcfapi, postMessage-relais, en een encoder voor het Core-segment in JavaScript. De gepubliceerde configuratie bevat geen enkel bezoekersgegeven — dat is wat de verenigbaarheid met een paginacache mogelijk maakt. Filter: owc_tcf_stub_config.

Er bestaat een PHP-spiegel van de encoder, maar die heeft geen enkele aanroeper in de plugin: hij is voorbehouden aan integraties en tests, met de waarschuwing hem nooit in cachebare HTML af te drukken.

Wat er wordt gecodeerd

Alleen het Core-segment, in base64url zonder padding. De vendorsectie wordt gecodeerd als bitveld of als reeksen: beide groottes worden gemeten en de kleinste wint. De vendorindex die naar de browser gaat, gebruikt een compact eigen formaat, met een hard plafond waarboven de index wordt geleegd (de vendortoestemmingen gaan dan verloren, de lijstversie blijft behouden).

Koppeling categorieën → TCF-doelen

CategorieDoelen
necessarygeen — buiten het TCF-bereik, wat garandeert dat “Alles weigeren” geen toestemming voor doel 1 kan opleveren
functional1
analytics1, 8, 9, 10
marketing1, 2, 3, 4, 7
preferences1, 5, 6, 11
social1

Filter: owc_tcf_purpose_map.

Gerechtvaardigd belang van de uitgever: alleen de doelen 2, 7, 8, 9, 10 en 11 worden behouden, omdat TCF v2.2 het gerechtvaardigd belang verbiedt voor de doelen 1, 3, 4, 5 en 6. Elke andere waarde van tcf_publisher_purposes_li wordt stilzwijgend weggelaten.

Een vendor is toegestaan zodra minstens één van de doelen die hij onder de grondslag “toestemming” opgeeft, is toegestaan.

De speciale functies

Er worden slechts twee entries ondersteund: 1 — gebruik van precieze geolocatiegegevens en 2 — actief scannen van apparaatkenmerken. Geef in tcf_special_features op welke u gebruikt; de lijst wordt tot deze catalogus teruggebracht.

Ze verschijnen als echte vinkvakjes in het voorkeurenpaneel. “Alles accepteren” vinkt ze niet aan: ze vereisen een eigen uitdrukkelijke opt-in. “Alles weigeren” en “Do Not Sell” vinken ze uit. Ze leven in de TC-string zelf, en worden bij het opnieuw openen van het paneel vanuit de TCF-API opnieuw ingelezen.

De TCF-laag in het paneel

Ze wordt alleen gerenderd als de module actief is, dat wil zeggen tcf_enabled en een bruikbaar CMP ID. Twee blokken:

  • Doelen: alleen die welke bereikbaar zijn via een niet-verplichte categorie en die in de catalogus van de vendorlijst zijn benoemd. Een schakelaar die vanzelf terugspringt zou erger zijn dan geen schakelaar. Een doel omschakelen schrijft naar alle categorieën die het opgeven, waarna de weergave opnieuw wordt afgeleid.
  • De opgegeven speciale functies.

Er wordt niets in de string gecodeerd wat niet is getoond.

De JavaScript-API

window.__tcfapi( command, version, callback, parameter );
// commando's: ping, getTCData, getInAppTCData,
//             addEventListener, removeEventListener, getVendorList

window.__owcTcfUpdateState( tcData );        // vervangt de data en verwittigt de listeners
window.__owcTcfRefresh();                    // herberekent en stuurt 'useractioncomplete'
window.__owcTcfUiShown(); window.__owcTcfUiHidden();
window.__owcTcfSetSpecialFeatures( [ 1, 2 ] );

Aanvaarde versies: afwezig, null, 2, '2', 2.2, '2.2'. Elke andere waarde geeft callback(null, false).

gdprApplies

Wordt aan de clientzijde beslist. Staat de regiodetectie uit, dan is de waarde true. Anders wordt het land van de bezoeker vergeleken met de lijst van 31 codes waar de AVG in TCF-zin van toepassing is (27 EU-landen, plus IS, LI, NO, GB en CH). Faalt gesloten: een onbekend land geeft true.

De cookie euconsent-v2

Wordt uitsluitend aan de clientzijde geschreven, en alleen wanneer de module klaar is en de bezoeker heeft gehandeld. Anders wordt de cookie gewist. Haar duur is die van de toestemming, afgetopt op 13 maanden (aanbeveling van IAB / CNIL). SameSite=Lax, Secure bij HTTPS.

Grenzen van de TCF-module — absoluut te lezen

  1. Dit is geen bij IAB Europe geregistreerde CMP. Ze vereist uw eigen CMP ID, en zelfs daarmee verplicht het TCF-beleid een geregistreerde CMP om keuzes te tonen op doelniveau én op vendorniveau. Hier volgen de doelschakelaars de categorieën en is er geen enkele keuze op vendorniveau: vendors mogen dit signaal terecht weigeren. Het beheerscherm zegt dat en spreekt van een “CMP-compatibele modus (niet officieel)”, die door de meeste SSP's in ontwikkeling wordt aanvaard maar in echte productie in de EER wordt geweigerd. Als advertentie-inkomsten onder TCF voor u meetellen, gebruik dan een gecertificeerde CMP.
  2. Alleen het Core-segment: geen segment disclosedVendors, allowedVendors of publisherTC.
  3. Er wordt geen enkele publisher restriction uitgestuurd: de bijhorende teller staat altijd op 0 en het object met de restricties is leeg.
  4. purposeOneTreatment en useNonStandardTexts staan altijd op false, isServiceSpecific staat altijd op true — er is geen globale scope — en het toestemmingsscherm is altijd 0.
  5. getVendorList negeert de versieparameter en geeft altijd de gecachete lijst terug.
  6. De toestemmingen van de uitgever kopiëren de algemene doelen: geen aangepaste doelen.
  7. Slechts twee speciale functies.
  8. De module vereist een werkende cron en uitgaand HTTPS. Zonder gecachete lijst antwoordt de API cmpStatus: 'error' en geeft de publieke route 503.

Global Privacy Control

GPC is een signaal dat de browser verstuurt — de header Sec-GPC: 1 en de eigenschap navigator.globalPrivacyControl. De plugin honoreert het standaard (gpc_honor), maar de behandeling ervan hangt af van het juridische regime, en dat is het belangrijke punt.

Onder de Amerikaanse profielen: bindend

Het signaal wordt onder de profielen ccpa en us_generic als een bindende universele opt-out behandeld. Australië is uitdrukkelijk uitgesloten: opt-outregime, maar het erkent GPC niet.

De schrijfactie aan de serverzijde gebeurt pas na een volledige reeks controles, in deze volgorde:

  1. gpc_honor staat aan;
  2. het signaal is aanwezig — de header Sec-GPC, met terugval op X-Sec-GPC omdat sommige proxy's en CDN's de header hernoemen; alleen de exacte waarde 1 telt;
  3. het effectieve profiel is ccpa of us_genericanders wordt er niets geschreven;
  4. het is een gewone paginaweergave: geen beheer, geen cron, geen AJAX, geen REST, geen XML-RPC, geen WP-CLI, en de methode is GET;
  5. de headers zijn nog niet verstuurd — een cookie die u niet kunt plaatsen, is een beslissing die u bij elk request opnieuw zou registreren;
  6. de sessiemarkering owc_gpc ontbreekt: één keer per surfsessie;
  7. de bezoeker lijkt niet op een bot (lege user-agent, of eentje die bot, crawl, spider, slurp, monitor, uptime, pingdom, headless, preview, curl/, wget, python-, java/, go-http, okhttp, httpclient, libwww of facebookexternalhit bevat);
  8. de al opgeslagen keuze voldoet nog niet aan het signaal — anders wordt alleen de browser gemarkeerd;
  9. de gedeelde rate limit wordt gerespecteerd: 30 per uur en per IP;
  10. de browser wordt vóór de schrijfactie gemarkeerd, zodat een mislukking geen replaylus wordt.

Het event wordt geregistreerd als gpc_opt_out met de bron auto. De pagina-URL wordt opgebouwd uit de URL van de site en het gevraagde pad — nooit uit de header Host, nooit uit de Referer.

Reikwijdte: alle optionele categorieën gaan op false; de verplichte categorieën blijven toegestaan. Dat is een ruime lezing van het begrip verkoop of deling.

Transparantie: overschrijft GPC een uitdrukkelijk geregistreerde keuze, dan zet een cookie van vijf minuten aan de clientzijde een melding onderaan het scherm in gang die dat uitlegt, met een knop “mijn voorkeuren beheren” die het paneel opent, en een sluitknop.

Onder de AVG-profielen en verwanten: enkel een aanwijzing

Buiten de Amerikaanse opt-outregimes wordt het signaal als een aanwijzing behandeld, nooit als een toestemming:

  • het attribuut data-owc-gpc="1" wordt op <html> gezet;
  • Consent Mode zet ad_storage, analytics_storage, ad_user_data, ad_personalization en personalization_storage op denied;
  • alle optionele categorieën gaan op false, enkel in het geheugen;
  • given blijft onwaar, de banner blijft zichtbaar, en er wordt niets geregistreerd.

Een interne vlag verhindert dat de runtime de bezoeker vertelt dat hij “al opt-out” is terwijl noch de cookie noch het register dat zegt. De verantwoording staat in de code: GPC is geen juridisch erkend signaal onder de AVG en ePrivacy, de Zwitserse nFADP, de LGPD, POPIA, PIPL, DPDP of Wet 25.

De twee cookies die deze module plaatst

CookieDuurRol
owc_gpcsessieMarkeert dat het signaal voor deze browser al is behandeld
owc_gpc_notice300 sZet de melding “GPC heeft uw keuze vervangen” in gang

Beide staan op path=/, SameSite=Lax, Secure bij HTTPS, en zijn leesbaar voor scripts.

Uitbreidingspunt

add_action( 'owc_gpc_honored', function ( array $categories ) {} );

De regiodetectie

De regiodetectie past op elke bezoeker het profiel van diens rechtsgebied toe in plaats van het geconfigureerde profiel van de site. Ze staat standaard uit.

InstellingStandaard
geo_enabledfalse
geo_default_profilegdpr
geo_mmdb_enabledfalse

De detectiecascade

De eerste treffer wint:

  1. Cloudflare (CF-IPCountry) — alleen als de site heeft verklaard achter Cloudflare te draaien;
  2. AWS CloudFront (CloudFront-Viewer-Country) — dezelfde voorwaarde;
  3. GEOIP_COUNTRY_CODE, de variabele die de server zelf schrijft (mod_geoip, ngx_http_geoip) en die de client dus niet kan vervalsen; en daarna, als er een proxy is verklaard, de varianten in HTTP-headers;
  4. Lokale MaxMind-database, als geo_mmdb_enabled aan staat;
  5. Niets.

Zonder een uitdrukkelijke verklaring in wp-config.php wordt geen enkele HTTP-landheader geloofd: die zijn door de client te vervalsen. Zie Installatie voor de constanten.

De landcode wordt gevalideerd tegen de officieel toegewezen allowlist ISO-3166-1 alpha-2 (ongeveer 249 codes die hard zijn opgesomd). Gebruikers- of gereserveerde codes — XX, ZZ, de T1 van Tor-uitgangen — worden geweigerd.

Falen gesloten

Zonder gedetecteerd land leest de plugin geo_default_profile, maar degradeert ze elk opt-outprofiel naar gdpr. Een geo_default_profile die op ccpa, us_generic of au staat, wordt dus genegeerd voor niet-gedetecteerde bezoekers — en het beheerscherm biedt in die keuzelijst alleen opt-inprofielen aan, in plaats van een keuze te tonen die stilletjes wordt genegeerd.

De reden is rechttoe rechtaan: zonder dat vangnet zou een site die in Amerikaans Engels is geconfigureerd elke EER-bezoeker in een opt-outregime plaatsen, met trackers die als “toegestaan” worden gemeld zonder enige toestemming.

Geen terugval op de locale van de site: de taal van een site zegt niets over de plaats van haar bezoeker.

De cookie owc_geo

  • Duur 24 uur, path=/, SameSite=Lax, Secure bij HTTPS, leesbaar voor scripts — de front-end bootstrap heeft dat nodig.
  • Inhoud: het land, het profiel, een tijdstempel en een authenticatiecode. Nooit een IP-adres.
  • Bij het lezen: begrensde omvang, land gevalideerd tegen de ISO-lijst, tijdstempel binnen het venster, code in constante tijd geverifieerd. Het profiel wordt altijd aan de serverzijde opnieuw berekend uit het land: een bezoeker kan zijn juridische regime niet kiezen.
  • Alleen een echte detectie wordt gecachet. De terugval “falen gesloten” wordt nooit onthouden.
  • Wordt alleen geschreven bij een front-endrequest, en alleen als de detectie aan staat.

De koppeling land → profiel

LandProfiel
De 30 EER-landengdpr
GB, JE, GG, IMuk_pecr
CHch_nfadp
BRlgpd
ZApopia
CN, HKpipl
INdpdp
AU, NZau
CAquebec
USccpa
Al de restDe terugval, gedegradeerd naar opt-in

Hongkong valt in werkelijkheid onder zijn eigen lokale verordening; het wordt uit voorzichtigheid als PIPL behandeld. Nieuw-Zeeland wordt als Australië behandeld. Heel Canada krijgt quebec — het strengste regime wint. De Verenigde Staten krijgen ccpa, dat als profiel voor de andere staten dienstdoet.

De cachebeperking — wat de detectie niet doet

Het geolokaliseerde profiel verandert de gerenderde HTML niet. Het herschrijven van het profiel geldt enkel voor de twee REST-routes voor bezoekers, /owc/v1/consent en /owc/v1/state. Het renderen van pagina's, de beheerroutes, wp-admin, de cron en WP-CLI houden het geconfigureerde profiel. Elk antwoord dat van die herschrijving gebruik heeft gemaakt, wordt als niet-cachebaar gemarkeerd.

Het is de front-end runtime die het regime kiest, aan de clientzijde, op basis van de cookie. Aanvaard gevolg: het regime dat op een bezoeker wordt toegepast hangt af van JavaScript, en een bezoeker wiens allereerste pagina uit de cache komt, wordt onder het geconfigureerde profiel van de site behandeld zolang de cookie niet bestaat.

De ingebouwde MaxMind-lezer

De plugin brengt haar eigen MMDB-lezer mee, geschreven in pure PHP, zonder Composer-afhankelijkheid — een plugin die op WordPress.org wordt gepubliceerd, mag het officiële pakket niet meeleveren.

  • Alleen lezen, alleen land. Geen stad, geen ASN.
  • Begrensde in- en uitvoer: lezen in kleine blokken, nooit een bestand van meerdere megabytes in het geheugen laden.
  • Werpt nooit een exception: een ontbrekend, onleesbaar, afgekapt, beschadigd of vijandig bestand geeft null, en de detectie valt terug op haar strenge profiel.
  • Interne vangnetten op de diepte van het doorlopen, het decodeerbudget en de omvang van de payload. De metadata worden gecachet in een transient waarvan de sleutel de omvang en de datum van het bestand bevat: uw maandelijkse download vervangen maakt de cache vanzelf ongeldig.
  • registered_country en represented_country worden bewust genegeerd. Dat is de gedocumenteerde vorm van anonieme proxy-, VPN- en satellietreeksen, waar het registratieland slaat op waar de provider het blok heeft geregistreerd, niet op de bezoeker. Ze gebruiken zou een EER-bezoeker achter een Amerikaanse VPN in een opt-outregime plaatsen.
  • Laadt een andere plugin al een GeoIP2-lezer, dan wordt die als terugval na de eigen lezer gebruikt.

Waar het bestand hoort: wp-content/uploads/ow-geoip/GeoLite2-Country.mmdb. Het pad is filterbaar via owc_geo_mmdb_path, met validatie tegen directory traversal.

Belangrijke grens: de plugin downloadt de database niet en biedt geen enkel uploadscherm. U moet het bestand bij MaxMind halen, het zelf plaatsen en het zelf bijwerken. Daar staat tegenover dat er niets naar MaxMind wordt gestuurd: de opzoeking is volledig lokaal, het IP-adres dient enkel als opzoekpunt en wordt daarna uitdrukkelijk vernietigd — nooit gelogd, nooit opgeslagen, nooit in de cookie geschreven.

Grenzen van de regiodetectie

  1. Standaard uit, en de MaxMind-variant vereist een bestand dat u zelf aanlevert.
  2. Landheaders worden genegeerd zolang u uw proxy niet hebt verklaard.
  3. Geen onderscheid per staat in de Verenigde Staten, en evenmin per provincie in Canada.
  4. Het regime van de bezoeker hangt af van JavaScript en van de cookie van 24 uur.
  5. Het profiel us_generic wordt nooit automatisch toegekend.

De OW Forms-integratie

Exacte reikwijdte: deze integratie mikt op de zusterplugin OW Forms, en op niets anders. Er is geen enkele integratie met Contact Form 7, Gravity Forms of WPForms.

Instelling: forms_integration_enabled, standaard aan, maar de integratie doet niets als OW Forms niet actief is.

Wat ze oplevert

1. De koppeling toestemming ↔ inzending. Bij elke opgeslagen inzending wordt een regel geschreven in een tabel die aan OW Consent toebehoort — het schema van OW Forms wordt nooit aangeraakt — met daarin:

  • het pseudonieme token van de bezoeker (32 hexadecimale tekens, nooit het e-mailadres);
  • de identificator van de recentste registerregel voor dat token;
  • de toegestane categorieën en het geldende compliance-profiel;
  • de volledige hash van de documenten die op dat moment gepubliceerd waren;
  • het tijdstempel van de toestemming;
  • de stand van het AVG-vinkje van het formulier en het exacte label dat ernaast stond.

Dat laatste is in de praktijk het nuttigste: u kunt niet alleen aantonen dat het vakje was aangevinkt, maar ook wat ernaast stond.

2. De wiscascade. Een geverifieerd wisverzoek delegeert de verwijdering aan de routine van OW Forms — die ook de geüploade bestanden verwijdert — en verwijdert daarna de koppelregels. De integratie merkt op of OW Forms al zijn eigen listener op dezelfde actie heeft aangehaakt, en beperkt zich in dat geval tot het opschonen van haar koppelingen.

3. De privacytools van WordPress. Er worden een exporter en een eraser geregistreerd, zodat Gereedschap → Persoonsgegevens exporteren / wissen de inzendingen van OW Forms dekt — en het rechtenportaal van OW Consent ook, want dat doorloopt dezelfde hooks. De export neemt de echte veldlabels over en voegt de toestemmingsvelden toe. Elke geëxporteerde waarde is beveiligd tegen injectie van spreadsheetformules en wordt ingekort.

4. De bewaartermijn. Op de dagelijkse cron worden koppelingen ouder dan retention_form_data_days (standaard 1095 dagen, grenzen 1 tot 3650) in batches van 500 verwijderd, met een opruiming van de wezen. Alleen de regels van OW Consent worden aangeraakt: de bewaring van de inzendingen zelf hoort bij OW Forms.

Grenzen

  1. Er wordt geen enkel inzendingsevent in het geketende register geschreven. De koppelregel verwijst naar een bestaande entry, ze maakt er geen aan — de keten mag niet van buitenaf worden beschreven.
  2. Een inzending koppelen is onmogelijk als OW Forms zo is ingesteld dat het inzendingen niet bewaart: er is dan geen enkele regel om aan vast te haken.
  3. Geen enkele retroactieve inhaalslag: de koppeling begint bij de eerste inzending na de update.
  4. De koppeling gebeurt op de gesalte hash van het adres die OW Forms berekent, met terugval op het adres in klare tekst dat OW Forms ook bewaart. Dit is geen zero-knowledge-schema: het adres blijft in klare tekst bij OW Forms, omdat u de persoon moet kunnen antwoorden.
  5. De integratie wijzigt nooit de plugin OW Forms, haar tabellen of haar opties.
  6. Is OW Forms actief maar te oud om zijn wisroutine beschikbaar te stellen, dan antwoordt de eraser uitdrukkelijk dat de inzendingen niet zijn aangeraakt.

De zwevende knop

Een kleine blijvende knop laat toe het voorkeurenpaneel op elk moment opnieuw te openen. Dat is de eis van artikel 7(3) — intrekken moet even eenvoudig blijven als toestemmen — en van de CNIL-beslissing 2020-091, die een mechanisme vraagt dat vanaf elke pagina bereikbaar is.

Bestaansvoorwaarden

De knop vereist beide instellingen: floating_button_enabled en banner_enabled. Het voorkeurenpaneel en de JavaScript-API leven in de runtime van de banner; zonder banner zou de knop een besturingselement zijn dat niets doet.

Hij stopt ook in de beheeromgeving, op een feed, op robots.txt, en als wp_head niet is afgevuurd.

Opties

InstellingWaardenStandaard
floating_button_enabledbooleantrue
floating_button_positionbottom-left, bottom-right, top-left, top-rightbottom-left
floating_button_stylepill, iconpill
floating_button_labelvrije tekstCookies

De knop heeft geen eigen thema-instelling: hij neemt banner_style over, zodat beide oppervlakken hetzelfde palet volgen.

Gedrag en toegankelijkheid

Het is een echte <button>, dus toetsenbordtoegankelijk. Hij draagt aria-haspopup="dialog" en een aria-label dat vertaalbaar maar niet instelbaar is (“Manage my cookie choices”): alleen het zichtbare label stelt u in. In de modus icon is het label visueel verborgen maar wordt het nog altijd door schermlezers voorgelezen.

Hij wordt met het attribuut hidden gerenderd; het is JavaScript dat beslist hem te tonen. Hij is zichtbaar in alle toestanden behalve wanneer de banner of het paneel al op het scherm staat — ook meteen na een keuze op de huidige pagina.

Een klik opent het voorkeurenpaneel. Bestaat de API van de banner niet, dan valt de knop terug op het fragment #owc-preferences, en vuurt hij het event desnoods met de hand opnieuw af als dat al het huidige fragment is.

z-index 99990, verborgen bij het afdrukken, ondersteuning van de modus voor hoog contrast en van prefers-reduced-motion, verschuiving onder de adminbalk in de bovenste positie.


Shortcodes

De plugin declareert twee shortcodes, en niet meer dan dat. Noch de banner noch de zwevende knop heeft er een: die verschijnen overal of nergens, gestuurd door hun instellingen.

[owc_dsar_form] — het rechtenportaal

[owc_dsar_form]
[owc_dsar_form types="access,erasure,portability" title="Mijn rechten" submit_label="Versturen"]
AttribuutStandaard
typesaccess,rectification,erasure,portability,restrict,object,optout
title“Mijn rechten op mijn persoonsgegevens uitoefenen”
submit_label“Mijn verzoek versturen”

De gevraagde types worden doorsneden met die welke de server aanvaardt. Het type withdraw wordt door het formulier nooit aangeboden; zie Het rechtenportaal.

[owc_dnsmpi] — de CCPA-opt-out

[owc_dnsmpi]
[owc_dnsmpi label="Verkoop of deel mijn persoonsgegevens niet" class="footer-link"]
AttribuutStandaard
label“Do Not Sell or Share My Personal Information”
classowc-dnsmpi

Een klik voert de opt-out uit; zie De CCPA-opt-out. Laat u ccpa_inject_footer aan staan, dan wordt de link al in de footer geïnjecteerd en dient deze shortcode alleen om hem elders te plaatsen.

Het paneel heropenen vanuit een menu of een link

Daar is geen shortcode voor, omdat een klasseattribuut volstaat:

<a href="#owc-preferences" class="owc-open-preferences">Mijn cookies beheren</a>
<button type="button" data-owc-open>Mijn voorkeuren</button>
<a href="#owc-dnsmpi" data-owc-dnsmpi="1">Verkoop mijn gegevens niet</a>

Dat is de aanbevolen manier om een item “Cookies” aan uw footermenu toe te voegen.


REST API

Alle routes leven onder de namespace owc/v1, op de gebruikelijke REST-basis (https://voorbeeld.com/wp-json/owc/v1/…). Dertien routes in totaal: twaalf geregistreerd door de REST-module, plus de route van de vendorlijst die de TCF-module registreert.

Publieke routes

Geen authenticatie. Hun beveiliging steunt op de origincontrole, een token dat eigen is aan de plugin, en de rate limits die verderop worden beschreven.

MethodePadParametersRol
GET/nonceaction (standaard wp_rest)Geeft een vers token terug. Antwoord: {nonce, action, header: "X-OWC-Nonce", ttl: 43200}, met no-store en Vary: Cookie
POST/consentevent (standaard save_preferences), source (standaard banner), categories (vereist), page_url (standaard ''), cookie_unreadable (boolean, standaard false)Registreert een keuze, plaatst de cookie en schrijft de registerregel
GET/stateDe status van enkel de aanroeper: {given, categories, profile, at}. Nooit het bezoekerstoken, nooit het IP-adres, nooit de pagina-URL. no-store + Vary: Cookie
GET/tcf/gvlServeert de gecachete Global Vendor List. ETag, Cache-Control: public, max-age=86400, 304 bij een conditioneel request, 503 + Retry-After: 300 als er niets in de cache zit
POST/dsartype (vereist), email (vereist, e-mailformaat), message (standaard '', max. 2000 tekens)Dient een verzoek van een betrokkene in. Antwoord {ok, mail_sent, message}nooit de identificator

Aanvaarde waarden:

  • event: accept_all, reject_all, save_preferences, withdraw, auto, gpc_opt_out
  • source: banner, preferences, footer_link, api, auto
  • type (DSAR): access, rectification, erasure, portability, restrict, object, optout, withdraw
  • categories: een object, maximaal 32 entries, uitsluitend scalaire waarden
  • action (nonce): uitsluitend wp_rest

Beheerroutes

Alle vereisen de capability manage_options.

MethodePadParametersRol
GET/ledgerpage (≥1, standaard 1), per_page (1–200, standaard 50), visitor_token (32 hex), from, toLeest het register. De filters die werkelijk zijn toegepast, komen mee in het antwoord
GET/ledger/verifyControleert de keten van begin tot eind
GET/settingsDe effectieve instellingen
POST/settingsvrije JSON-bodySchrijft de instellingen. Antwoord {ok, updated, rejected, settings}
POST/scanner/runStart een scan
POST/scanner/ingestJSON-body {page, findings[]}Ontvangst van de waarnemingen van de probe
GET/scanner/findingspage, per_page (1–200, standaard 50), filter (categorieslug of unknown)Toont de vondsten
POST/policies/generatetype (vereist), publish (boolean, standaard false)Genereert een document
GET/policies/previewtype (vereist)Toont een preview van een document

/scanner/ingest vereist bovendien een geldige wp_rest-nonce, in de header X-WP-Nonce of als parameter _wpnonce — omdat sendBeacon geen header kan meegeven.

/settings aanvaardt bij het schrijven alleen de sleutels die als echte instellingen zijn gedeclareerd: een filter kan synthetische sleutels injecteren die dat niet zijn. Elke waarde wordt afzonderlijk opgeschoond. Geen enkele bekende sleutel levert een 400 op.

/ledger toetst de capability opnieuw in de handler, als verdediging in de diepte tegen een permission_callback die elders is gefilterd.

Het beveiligingsmodel van de publieke schrijfacties

De vaststelling vooraf is eenvoudig: een nonce kan niet in cachebare HTML leven. /consent en /dsar hebben daarom een soepele toegangscontrole, beschermd door vier lagen in een volgorde die ertoe doet.

  1. Origincontrole op dezelfde host. Gratis, deterministisch, verbruikt geen enkel budget.
  2. Een plugineigen token in de header X-OWC-Nonce — dat is een bewijs, nooit een veto. De keuze om X-WP-Nonce niet te gebruiken is bewust: de WordPress-core onderschept die header vóór elke toegangscontrole van de route en weigert het volledige request als hij hem niet valideert. Een verlopen token dat door een cache wordt geserveerd, zou dus een schrijfactie doden die het endpoint zonder enig token wél had aanvaard.
  3. Falen gesloten: geen origin en geen geverifieerd token geven een 403 owc_missing_origin.
  4. Rate limiting als laatste. Kwam die eerst, dan zou een verkeerde configuratie twee tokens per klik opgebruiken en op een 429 uitdraaien, waardoor de configuratiefout achter een rate limit verborgen zou blijven.

Aanvaarde hosts: die welke WordPress opgeeft (site-URL, WordPress-URL, REST-basis) met hun www./apex-tweeling, plus de host uit de Host-header van het huidige request en diens tweeling. “Dezelfde origin” betekent “de origin komt overeen met de host waarmee de browser verbinding maakte”, niet “komt overeen met wat WordPress in de database heeft” — anders zou elke site die via een previewdomein, een alias, een stagingnaam, een gemapt domein in multisite of achter een proxy die Host herschrijft wordt bereikt, stilzwijgend worden geweigerd, zonder enige kans op zelfherstel. Filter: owc_allowed_request_hosts.

CORS wordt bewust aan de WordPress-core overgelaten.

/nonce weigert uitdrukkelijk de JSONP-vorm: zonder die weigering zou een externe pagina via een simpele <script src>, die niet aan de origincontrole is onderworpen, het levende token van een langskomende beheerder kunnen oogsten.

De rate limits

BucketBudgetVensterSleutel
nonce1205 minutenIP-adres
consent305 minutenIP-adres
dsar31 uurIP-adres
dsar_email324 uurbeoogd e-mailadres
dsar_global301 uurde hele site
gpc301 uurIP-adres
tcf_gvl101 uurIP-adres
  • Vast venster, niet glijdend: een bucket die verkeer krijgt, verloopt altijd uiteindelijk.
  • De opgeslagen sleutel is een gesalte hash: geen enkel IP-adres of e-mailadres in klare tekst staat in de opties of in de objectcache.
  • IP-normalisatie: IPv4 blijft behouden, IPv6 wordt tot /64 ingekort — de client bepaalt elke bit van de interface-identifier, dus met een volledige sleutel zou hij bij elk request een vers budget kunnen aanmaken.
  • Gedeelde identiteit: achter een CDN of een proxy die niet via OWC_TRUSTED_PROXY is verklaard, komen alle bezoekers met hetzelfde adres binnen. Het budget wordt dan per bezoeker opgesplitst, met een ruimere teller op het gedeelde adres. Dat is een grovere begrenzing: verklaar uw proxy.
  • Filter owc_throttle_max( $max, $bucket, $window )0 teruggeven schakelt de limiet uit.
  • De limiter faalt open als de objectcache niet beschikbaar is: liever niet begrenzen dan iemand verhinderen zijn rechten uit te oefenen.

De route /consent in detail

  • headers_sent() wordt als allereerste gelezen. Is de output al begonnen, dan kan de servercookie niet worden geplaatst: de regel wordt dan toch geschreven en het antwoord blijft 200, met cookie_set: false en een descriptor cookie (name, value, ttl, path, samesite, secure) die de client zelf plaatst.
  • De categorieën die de site niet aanbiedt, worden teruggegeven in dropped_categories, niet stilzwijgend genegeerd.
  • page_url wordt gevalideerd tegen de hosts van de site, met terugval op een Referer die op dezelfde manier is gevalideerd, en anders op de lege string. De sleutel is altijd aanwezig, zodat het register niet zelf de ruwe Referer gaat opzoeken.
  • Staat het register aan en wordt de schrijfactie geweigerd, dan wordt de cookie ingetrokken en is het antwoord een 503 owc_ledger_write_refused. Er wordt niets opgeslagen en er wordt geen enkele tracker vrijgegeven.
  • cookie_unreadable is pure telemetrie: er wordt geen enkele validatiecontrole op toegepast, juist zodat een diagnostisch veld nooit een toestemmingsschrijfactie kan weigeren. Het enige effect is een notitie in het weigeringslogboek.

Standaardantwoord: {ok, state, dropped_categories, logged, cookie_set[, cookie]}. Het veld logged is null wanneer het register uit staat.

Foutcodes

CodeHTTPBetekenis
owc_bad_param400Ongeldige parameter
owc_bad_categories400Object categories misvormd, te groot of niet-scalair
owc_bad_email400Ongeldig e-mailadres
owc_bad_dsar400Misvormde DSAR-inzending (honeypot inbegrepen)
owc_dsar_not_attested400Verklaring uit art. 12.6 ontbreekt
owc_no_settings400Geen enkele bekende instellingssleutel in de body
owc_missing_origin403Geen origin en geen token: falen gesloten
owc_bad_origin403De opgegeven origin is geen host van deze site — het antwoord somt tot tien aanvaarde hosts op
owc_bad_nonce403Ongeldige nonce op /scanner/ingest
owc_jsonp_forbidden403JSONP-vorm geweigerd op /nonce
owc_forbidden401/403Onvoldoende capability
owc_dsar_disabled404Het rechtenportaal staat aan de serverzijde uit
owc_no_template404Geen sjabloon voor dit type en dit profiel
owc_rate_limited429Rate limit bereikt
owc_consent_failed500Toepassen van de toestemming mislukt
owc_dsar_store_failed500Wegschrijven van het verzoek mislukt
owc_gen_failedwisselendDocumentgeneratie geweigerd (ontbrekende velden, taalslot)
owc_ledger_write_refused503Het register weigerde te schrijven — er wordt niets geregistreerd, er wordt niets gedeblokkeerd

Het weigeringslogboek

De laatste twintig geweigerde schrijfacties worden bewaard en op het dashboard getoond — alleen als het logboek niet leeg is. Er worden twee soorten onderscheiden:

  • een entry met een HTTP-status is een echte weigering: er is niets opgeslagen en de bezoeker heeft een fout gezien;
  • een entry met status 0 is een waarschuwing: de keuze is wel degelijk geregistreerd.

Het paneel toont naast elkaar de host die de origin opgeeft en de Host-header van het request. Dat is precies wat verschilt wanneer uw bezoekers op de ene host surfen terwijl WordPress met een andere is geconfigureerd — de meest voorkomende oorzaak van een toestemming die “niet wordt opgeslagen”.

Grenzen van de API

  1. /consent en /dsar aanvaarden anonieme schrijfacties, by design; de bescherming is de origin plus de rate limit, niet een nonce.
  2. De rate limiter faalt open zonder objectcache.
  3. De toestemmingscookie is host-only: een cookie die voor de apex is geplaatst, is onleesbaar vanaf een www.-pagina, en geen enkele CORS-header kan dat oplossen. De remedie is een schrijf-URL op dezelfde origin.
  4. /scanner/ingest aanvaardt alleen manage_options: de probe kan niet van een bezoeker komen.
  5. /settings heeft geen eigen nonce: het is manage_options plus de noncecontrole via cookie van de core.

Instellingenreferentie

Alle instellingen passen in één enkele optie, owc_settings, die automatisch wordt geladen omdat ze bij elke front-endpagina wordt gelezen.

Lezen en schrijven in PHP

$instellingen = OWC_Core::settings();               // effectieve instellingen (standaard + opgeslagen + filters)
$profiel      = OWC_Core::setting( 'compliance_profile' );
$ruw          = OWC_Core::stored_setting( 'compliance_profile' ); // negeert de herschrijving per bezoeker

OWC_Core::update_settings( array(
    'consent_renewal_months' => 6,
    'compliance_strict'      => true,
) );

update_settings() voegt alleen de aangeleverde sleutels samen bovenop het bestaande, nooit het volledige stel standaardwaarden. Dat is wat de optierij leeg laat blijven zolang u niets hebt aangepast, en dus de teksten de taal van de site laat volgen.

Het resultaat van settings() wordt gememoïseerd, met een sleutel gebaseerd op het geheel van de callbacks van het filter owc_settings: een module die haar filter na de eerste lezing registreert, maakt de memo ongeldig in plaats van genegeerd te worden. De memo wordt bij elke schrijfactie op de optie geleegd.

Standaardwaarden

[
    // Banner
    'banner_enabled'            => true,
    'banner_position'           => 'bottom-bar',   // bottom-bar | bottom-card | center-modal | top-bar
    'banner_style'              => 'auto',         // auto | light | dark
    'banner_accept_all'         => true,
    'banner_reject_all'         => true,
    'banner_preferences'        => true,
    'banner_close_x'            => false,          // kruisje = impliciete weigering
    'banner_show_logo'          => true,
    'text_title'                => 'We use cookies',
    'text_message'              => 'We use cookies to personalise content, measure audience, '
                                 . 'and provide social media features. You can accept or reject, '
                                 . 'and change your choice at any time.',
    'text_accept_all'           => 'Accept all',
    'text_reject_all'           => 'Reject all',
    'text_preferences'          => 'Customize',
    'text_save'                 => 'Save my choices',

    // Zwevende knop
    'floating_button_enabled'   => true,
    'floating_button_position'  => 'bottom-left',
    'floating_button_style'     => 'pill',
    'floating_button_label'     => 'Cookies',

    // Toestemming
    'consent_renewal_months'    => 12,             // 0 wordt omgezet naar 13, niet naar “nooit”
    'consent_policy_hash_check' => true,

    // Compliance
    'compliance_profile'        => 'gdpr',
    'compliance_strict'         => true,
    'geo_enabled'               => false,
    'geo_default_profile'       => 'gdpr',
    'geo_mmdb_enabled'          => false,

    // IAB TCF v2.2
    'tcf_enabled'               => false,
    'tcf_cmp_id'                => 0,
    'tcf_publisher_country'     => 'FR',
    'tcf_publisher_purposes_li' => [],
    'tcf_special_features'      => [],

    // CCPA / GPC
    'ccpa_inject_footer'        => true,
    'gpc_honor'                 => true,

    // Google Consent Mode v2
    'gcm_enabled'               => true,
    'gcm_ads_data_redaction'    => true,
    'gcm_url_passthrough'       => true,

    // Categorieën
    'cat_necessary_available'   => true,
    'cat_functional_available'  => true,
    'cat_analytics_available'   => true,
    'cat_marketing_available'   => true,
    'cat_preferences_available' => true,
    'cat_social_available'      => true,
    'cat_necessary_label'       => 'Necessary',
    'cat_necessary_desc'        => 'Strictly required for the site to function …',
    'cat_functional_label'      => 'Functional',
    'cat_functional_desc'       => 'Enhance the experience (chat, embedded videos, maps) …',
    'cat_analytics_label'       => 'Statistics',
    'cat_analytics_desc'        => 'Help us understand how you use the site (anonymously) …',
    'cat_marketing_label'       => 'Marketing',
    'cat_marketing_desc'        => 'Enable us to show you ads and content tailored …',
    'cat_preferences_label'     => 'Preferences',
    'cat_preferences_desc'      => 'Remember your interface choices (layout, saved filters).',
    'cat_social_label'          => 'Social & embeds',
    'cat_social_desc'           => 'Allow embedded social content (YouTube, Instagram, X) to load.',

    // Register
    'ledger_enabled'            => true,
    'ledger_retention_days'     => 1825,           // 5 jaar; 0 = onbeperkt
    'ledger_hash_algo'          => 'sha256',

    // Scanner
    'scanner_enabled'           => false,
    'scanner_frequency'         => 'weekly',
    'scanner_alert_email'       => '',
    'scanner_max_urls'          => 25,
    'scanner_timeout'           => 8,
    'scanner_probe_mode'        => 'admins',

    // Rechtenportaal
    'dsar_enabled'              => true,
    'dsar_email'                => '',
    'dsar_response_days'        => 30,
    'dsar_notify_email'         => '',
    'dsar_token_ttl_days'       => 7,

    // Blokkering
    'blocker_unknown_script_policy' => 'allow',
    'blocker_unknown_iframe_policy' => 'block',
    'blocker_allowlist'             => '',         // één host per regel
    'blocker_block_resource_hints'  => true,

    // Integratie en levenscyclus
    'forms_integration_enabled' => true,
    'auto_footer_menu_inject'   => false,
    'delete_data_on_uninstall'  => false,

    // Juridische identiteit
    'legal_company_name'         => '',
    'legal_company_legal_form'   => '',
    'legal_company_capital'      => '',
    'legal_company_address'      => '',
    'legal_company_email'        => '',
    'legal_company_phone'        => '',
    'legal_country'              => 'FR',
    'legal_company_reg_number'   => '',
    'legal_company_reg_place'    => '',
    'legal_company_vat'          => '',
    'legal_publication_director' => '',
    'legal_eu_representative'    => '',
    'legal_host_name'            => '',
    'legal_host_address'         => '',
    'legal_host_phone'           => '',
    'legal_host_url'             => '',
    'legal_profession_body'      => '',
    'legal_profession_title'     => '',
    'legal_profession_state'     => '',
    'legal_profession_rules'     => '',
    'legal_mediator_name'        => '',
    'legal_mediator_address'     => '',
    'legal_mediator_url'         => '',
    'legal_dpo_name'             => '',
    'legal_dpo_email'            => '',
    'legal_dpa_authority'        => '',            // bewust leeg: afgeleid uit land + profiel
    'legal_transfer_countries'   => '',
    'legal_third_parties_note'   => '',
    'legal_automated_decisions'  => false,
    'legal_automated_decisions_desc' => '',
    'legal_minor_age'            => 15,
    'legal_data_provision_note'  => '',
    'legal_currency'             => 'EUR',
    'legal_tax_label'            => 'TTC',
    'legal_business_type'        => 'auto',
    'retention_form_data_days'   => 1095,
    'retention_dsar_days'        => 1095,
    'policy_disclaimer'          => true,

    // Links
    'link_privacy_policy'        => '',
    'link_cookie_policy'         => '',
    'link_terms'                 => '',
    'link_legal_notice'          => '',
]

Gesloten lijsten

Een waarde buiten de lijst wordt op de standaardwaarde teruggezet.

InstellingAanvaarde waarden
banner_positionbottom-bar, bottom-card, center-modal, top-bar
banner_styleauto, light, dark
floating_button_positionbottom-left, bottom-right, top-left, top-right
floating_button_styleicon, pill
compliance_profile, geo_default_profilegdpr, ccpa, lgpd, popia, pipl, dpdp, quebec, uk_pecr, ch_nfadp, au, us_generic
ledger_hash_algosha256, sha3-256 (doorsneden met wat PHP ondersteunt)
scanner_frequencyhourly, twicedaily, daily, weekly
scanner_probe_modeadmins, off
legal_business_typeauto, vitrine, rental, ecommerce, services, saas, content
blocker_unknown_script_policy, blocker_unknown_iframe_policyallow, block

Grenzen van de gehele getallen

InstellingGrenzen
consent_renewal_months0 tot 13 (0 wordt als 13 behandeld)
dsar_response_days1 tot 30
dsar_token_ttl_days1 tot 90
ledger_retention_days0 tot 3650 (0 = onbeperkt)
scanner_max_urls1 tot 500
scanner_timeout1 tot 60
legal_minor_age13 tot 18
retention_form_data_days1 tot 3650
retention_dsar_days1 tot 3650
tcf_cmp_id0 tot 4095

Opschoning

  • text_message is het enige veld met rijke HTML; het aanvaardt de HTML die in een WordPress-bericht is toegestaan, en de banner rendert uiteindelijk alleen <a href target rel>.
  • Velden met meerdere regels: legal_company_address, legal_eu_representative, legal_host_address, legal_mediator_address, legal_third_parties_note, legal_automated_decisions_desc, legal_data_provision_note, blocker_allowlist, en elke sleutel die op _desc eindigt.
  • URL's: elke sleutel met het voorvoegsel link_ of het achtervoegsel _url, plus legal_profession_rules.
  • E-mailadressen: elke sleutel die email bevat.
  • Al de rest: platte tekst.
  • Een niet-scalaire waarde die op een scalaire instelling wordt aangeleverd, wordt genegeerd; de sleutel wordt niet geschreven.

Waar u wat instelt

TabbladInstellingen
Bannerbanner_*, text_*, floating_button_*, consent_renewal_months, consent_policy_hash_check
Compliancecompliance_profile, compliance_strict, gcm_*, blocker_*, ccpa_inject_footer, gpc_honor, tcf_*, geo_*, auto_footer_menu_inject, forms_integration_enabled, delete_data_on_uninstall, link_*
Legal identitylegal_*, retention_form_data_days, retention_dsar_days, policy_disclaimer
Categoriescat_*_available, cat_*_label, cat_*_desc
Audit ledgerledger_enabled, ledger_retention_days (het algoritme wordt getoond, niet gewijzigd)
Scannerscanner_*
DSAR requestsdsar_*

Tabellen

TabelInhoud
{prefix}owc_ledgerGeketend toestemmingsregister
{prefix}owc_dsarVerzoeken van betrokkenen
{prefix}owc_scannerVondsten van de scanner
{prefix}owc_scriptsSignaturencatalogus van de blokkering
{prefix}owc_form_linksKoppelingen toestemming ↔ OW Forms-inzending

Een PHP-helper owc_table( 'ledger' | 'dsar' | 'scanner' | 'scripts' ) berekent de naam opnieuw op basis van het huidige prefix: gebruik hem binnen een switch_to_blog(), want de constanten liggen voor de duur van het request vast.

Andere opties en transients

Opties: owc_settings, owc_version, owc_pending_upgrade, owc_upgrade_lock, owc_ledger_head, owc_ledger_trim, owc_ledger_key_source, owc_scanner_secret, owc_scanner_lock, owc_scanner_queue, owc_scanner_status, owc_scanner_alerted, owc_scanner_probe_status, owc_scanner_manual, owc_refusal_log, owc_tcf_status, owc_tcf_gvl_version, owc_forms_db_version, plus de migratievlaggen (owc_catalog_sanitized_v2, owc_match_target_migrated_v1, owc_ledger_hmac_migrated_v1, owc_inline_sig_labels_cleaned_v1, owc_scanner_secret_rotated_v1).

Transients: owc_catalog_<versie>, owc_invalid_patterns, owc_gvl_cache, owc_gvl_stub, owc_gvl_etag, owc_gvl_retry, owc_mmdb_<hash>, owc_policies_hash, owc_site_analysis, owc_scanner_last_run, owc_scanner_run_result, owc_scanner_pruned, owc_scanner_fail_alerted, owc_ledger_verify_result, owc_ledger_write_error, owc_policy_gen_result, owc_dsar_sla_alert, owc_dsar_admin_result, en de tellers voor de rate limiting.

Constanten die in wp-config.php worden herkend

ConstanteEffect
OWC_LEDGER_KEYHMAC-sleutel van het register, en de afgeleide sleutel van de regiodetectiecookie
OWC_TRUSTED_PROXY / OWC_TRUSTED_PROXIESCIDR-reeksen van de vertrouwde proxy's; zonder die constanten worden de headers met het client-IP genegeerd
OWC_GEO_TRUSTED_HEADERScloudflare, cloudfront, proxy of all
OWC_BEHIND_CLOUDFLARESnelkoppeling voor de landheader van Cloudflare
OWC_BEHIND_CLOUDFRONTSnelkoppeling voor de landheader van CloudFront

De developer hooks

Filters

FilterRol
owc_settingsEffectieve instellingen. Het antwoord moet afhangen van iets dat voor het hele request stabiel is
owc_banner_textsTeksten van de banner
owc_bootstrap_configStatische configuratie afgedrukt in <head>nooit gegevens per bezoeker
owc_scripts_catalogCatalogusregels vóór de validatie
owc_blocker_allowlistHosts die nooit worden geblokkeerd
owc_scanner_urlsTe scannen URL's (nadien opnieuw tot de host van de site beperkt)
owc_throttle_maxBudget van een rate limit; 0 schakelt ze uit
owc_allowed_request_hostsAanvaarde hosts voor een publieke schrijfactie
owc_allow_headerless_writeHeft het falen gesloten op voor schrijfacties zonder origin (standaard false)
owc_geo_mmdb_pathPad naar de MaxMind-database
owc_tcf_purpose_mapKoppeling categorieën → TCF-doelen
owc_tcf_stub_configStatische configuratie van de TCF-stub
owc_policy_templateRuwe tekst van een sjabloon — schakelt het taalslot uit
owc_policy_varsVariabelen van een document
owc_policy_htmlUiteindelijke HTML van een document
owc_dsar_typesAangeboden en aanvaarde soorten verzoeken
owc_dsar_form_noticeInformatietekst onder het formulier
owc_dsar_export_bundleInhoud van de portabiliteitsbundel
owc_dsar_show_fulfilment_panelWeergave van het uitvoeringspaneel

Acties

ActieSignatuur
owc_consent_updated( array $categories, string $event, string $source )
owc_consent_cookie_not_sent( array $cookie, string $event )
owc_ledger_lock_failed( string $event, string $token )
owc_ledger_write_failed( $error, string $event, string $token )
owc_ledger_anchor( array $anchor )
owc_ledger_pruned( int $deleted, int $days )
owc_catalog_updated
owc_gpc_honored( array $categories )
owc_tcf_inactive( string $reason )
owc_dsar_submitted( string $type, string $email, string $message )
owc_dsar_verified( int $id, array $row )
owc_dsar_fulfilled( int $id, string $what, $trace )
owc_dsar_mail_failed( int $id, string $email, string $kind )

Crons: owc_daily_maintenance, owc_ledger_retention, owc_scanner_run, owc_scanner_run_batch, owc_tcf_refresh_gvl, owc_run_upgrade.

Internationalisatie

Het tekstdomein is ow-consent, het pad /languages. De brontaal is het Engels. Het pakket levert het sjabloon ow-consent.pot en een volledige Franse vertaling mee.

Ter herinnering aan het hierboven beschreven mechanisme: zolang een label of een bannertekst niet in de instellingen is aangepast, volgt hij de taal van de site. Zodra u uw eigen waarde invoert, wordt die net zo geserveerd, ongeacht de taal van de bezoeker.

De teksten van de juridische documenten lopen niet via dat mechanisme: hun taal volgt het rechtsgebied, nooit de locale van de beheerder.


Probleemoplossing

De banner verschijnt niet

Loop de lijst in volgorde af:

  1. Staat banner_enabled aan? De badge bovenaan het beheerscherm zegt het.
  2. Roept uw thema wp_head() en wp_footer() aan? Zonder die twee weigert de banner zich af te drukken, in plaats van inerte markup te produceren.
  3. Heeft de bezoeker al een keuze gemaakt? De banner verschijnt alleen bij <html data-owc="none">. Test in een privévenster of met window.OWCBanner.reset().
  4. Heeft de bezoeker JavaScript? Zonder JavaScript blijft de banner verborgen — bewust — en neemt het <noscript>-blok het over.
  5. Serveert een paginacache een versie van vóór de activatie? Leeg de cache.

De banner komt op elke pagina terug hoewel de keuze is geregistreerd

Drie oorzaken, in volgorde van frequentie.

  1. De bezochte host verschilt van de host die in WordPress is geconfigureerd (www. tegenover apex, alias, stagingdomein). De browser weigert dan de cookie op te slaan terwijl WordPress 200 antwoordt. Open het dashboard: het weigeringslogboek toont de origin en de Host-header naast elkaar. De remedie is de site op één canonieke host serveren.
  2. consent_policy_hash_check staat aan en uw documenten zijn gewijzigd — dat is het gewenste gedrag, de bezoeker wordt na een wijziging van het beleid opnieuw bevraagd.
  3. U draait op een versie ouder dan 1.4.3. De cookie was toen twee keer gecodeerd en de browser kreeg hem niet meer gelezen, terwijl de server hem probleemloos las. Werk bij: de cookies die vóór de correctie zijn geschreven, blijven leesbaar.

“Uw keuze kon niet worden geregistreerd” — fout refused of 403

Kijk naar data-owc-code op het berichtelement, of naar het weigeringslogboek op het dashboard.

  • owc_bad_origin: de opgegeven origin is geen herkende host. Het antwoord somt de aanvaarde hosts op. Wordt uw site legitiem via meerdere namen bereikt, voeg ze dan toe met het filter owc_allowed_request_hosts.
  • owc_missing_origin: geen origin en geen token. Dat is typisch een privacy-extensie die de headers verwijdert, of een proxy die ze herschrijft.
  • owc_bad_nonce: betreft alleen de probe van de scanner, niet de toestemmingsschrijfactie — de nonce is op /consent nooit een veto.

Fout ratelimit — 429

De limieten liggen bewust laag op publieke schrijfacties. Twee oorzaken:

  1. Een niet-verklaarde CDN of proxy: al uw bezoekers komen met hetzelfde adres binnen en delen hetzelfde budget. Verklaar hem met OWC_TRUSTED_PROXY in wp-config.php.
  2. Een geautomatiseerde test die meer dan 30 schrijfacties in 5 minuten vanaf hetzelfde adres stuurt.

Een 503 owc_ledger_write_refused

Het register weigerde te schrijven, dus er is niets geregistreerd en er is geen enkele tracker vrijgegeven. Dat is het gewenste gedrag: een toestemming die u niet kunt bewijzen, mag u niet claimen.

  1. Controleer of de tabel {prefix}owc_ledger bestaat. Het tabblad Audit ledger toont een leesbaar bericht “nog niet geïnstalleerd” in plaats van een wit scherm.
  2. Controleer de schrijfrechten van de MySQL-gebruiker.
  3. Zet als laatste redmiddel ledger_enabled tijdelijk uit om de dienst te herstellen — in het besef dat u ondertussen het bewijs verliest.

Er laden nog steeds trackers vóór de toestemming

  1. Staat compliance_strict aan? Zonder die instelling worden alleen de door WordPress in de wachtrij gezette scripts behandeld.
  2. Staat de tracker in de catalogus? Een onbekend script van derden wordt standaard toegelaten. Start een scan en deel de vondst in: dat schrijft de blokkeerregel.
  3. Wordt hij door first-party JavaScript geïnjecteerd? De dynamische bewaking dekt maar twintig hosts. Voeg een catalogusregel toe op het domein van de tracker.
  4. Is het een cookie die via een Set-Cookie-header wordt geplaatst? Geen enkele clientblokkering kan die tegenhouden. Die moet u bij de bron aanpakken, aan de serverzijde.
  5. Zet blocker_unknown_script_policy op block — en test daarna de site zorgvuldig, want die instelling blokkeert elke niet-herkende derde partij.

Een legitieme integratie wordt geblokkeerd

Voeg haar host toe aan blocker_allowlist, één per regel, of via het filter owc_blocker_allowlist. Controleer ook blocker_unknown_iframe_policy: onbekende iframes worden standaard geblokkeerd, dat is het meest voorkomende geval.

Moet de integratie afhankelijk blijven van de toestemming, deel ze dan liever in de juiste categorie in: de visuele vervanging draagt een knop “Accepteer …” die haar met één klik deblokkeert.

De scanner vindt niets

  1. Staat hij aan? Hij staat standaard uit.
  2. Werkt WP-Cron? Met DISABLE_WP_CRON en zonder systeemcron gaat de geplande scan nooit af. Start er met de hand een om dat te controleren.
  3. Is de HTTP-loopback mogelijk? Een HTTP-authenticatie op een staging-omgeving of een firewall blokkeert de scan. Het dekkingspaneel toont de eerste fout.
  4. De serverscan voert geen enkel stukje JavaScript uit. Bezoek een pagina van de site als ingelogde beheerder, zodat de probe rapporteert wat een tagmanager injecteert.

Lees in alle gevallen het paneel “wat deze scan werkelijk dekt” vóór u conclusies trekt: het maakt onderscheid tussen “niets gevonden” en “niets gelezen”.

Het genereren van een document wordt geweigerd

Twee mogelijke oorzaken, en het bericht zegt welke:

  • Er zijn verplichte velden leeg — de lijst van ontbrekende sleutels wordt getoond. Vul het tabblad Legal identity aan.
  • Taalslot — u vraagt wettelijke vermeldingen of algemene voorwaarden voor een niet-Franstalig rechtsgebied. De knop is in dat geval verborgen. De enige weg is uw eigen tekst aanleveren via het filter owc_policy_template.

De DSAR-verificatiemail komt niet aan

Dat is bijna altijd de deliverability, niet de plugin.

  1. Het antwoord van de API bevat mail_sent: false wanneer het versturen is mislukt, en het formulier toont dan een specifiek bericht dat naar het contactadres verwijst.
  2. Installeer een SMTP-plugin. De functie mail() van PHP wordt door de meeste ontvangende servers geweigerd.
  3. Controleer SPF, DKIM en DMARC van uw domein. De plugin herschrijft nooit het afzenderadres — dat is wat SPF zou breken — ze zet enkel een Reply-To.
  4. Controleer of dsar_email een geldig adres is.

Zolang de e-mail niet is ontvangen en bevestigd, blijft het verzoek pending en start de wettelijke termijn niet.

De TCF-module wordt niet actief

Open het tabblad Compliance: een adminmelding noemt de oorzaak.

  • missing_cmp_id: tcf_cmp_id staat op 0. U moet een CMP ID aanvragen bij IAB Europe; de plugin levert er geen en weigert een string met identificator 0 uit te sturen.
  • CMP ID groter dan 4095: de waarde past niet in het veld van 12 bits van de string en zou een andere CMP aanduiden.
  • no_gvl: de Global Vendor List is nog niet gedownload. Ze komt via een dagelijkse cron; controleer of WP-Cron draait en of uw server vendor-list.consensu.org kan bereiken via uitgaand HTTPS.

Alle bezoekers krijgen het geconfigureerde profiel, ondanks de regiodetectie

  1. Staat geo_enabled aan? Het staat standaard uit.
  2. Hebt u uw proxy verklaard? Zonder OWC_BEHIND_CLOUDFLARE, OWC_BEHIND_CLOUDFRONT of OWC_GEO_TRUSTED_HEADERS in wp-config.php worden de landheaders genegeerd, omdat ze te vervalsen zijn.
  3. Staat het MaxMind-bestand op zijn plaats? De plugin downloadt het niet en biedt geen uploadscherm.
  4. Misschien is het normaal: de eerste weergave voor een bezoeker die uit de cache wordt bediend, gebruikt het geconfigureerde profiel zolang de cookie van 24 uur niet bestaat. Het dashboard toont een diagnoseblok met het gedetecteerde land, het toegepaste profiel en de bron.

De beheeromgeving meldt dat de sleutel van het register in de database leeft

Dat is de melding notice_ledger_key. Ze betekent dat AUTH_KEY en AUTH_SALT niet in wp-config.php staan — WordPress bewaart ze dan in de database — en dat OWC_LEDGER_KEY evenmin is gedefinieerd. De keten wordt verder opgebouwd, maar wie toegang heeft tot de database kan ze opnieuw ondertekenen: de aanspraak op onvervalsbaarheid houdt geen stand meer.

De remedie is OWC_LEDGER_KEY toevoegen in wp-config.php (en, nu u toch bezig bent, AUTH_KEY en AUTH_SALT). De verificatie onderscheidt daarna een sleutelrotatie van een herschrijving, dus die wijziging maakt uw geschiedenis niet ongeldig.

De plugin doet helemaal niets, met een rode melding

Uw WordPress is ouder dan 6.2. De plugin weigert te starten, en de melding zegt uitdrukkelijk dat er niets wordt geblokkeerd en dat er geen enkele toestemming wordt geregistreerd. Werk WordPress bij, of deactiveer de plugin en haal de banner ondertussen van uw pagina's.

Catalogusregels die “nooit kunnen afgaan”

Een adminmelding signaleert ze, en het tabblad Tracker catalogue heeft er een eigen filter voor. Vier mogelijke redenen: een leeg patroon of een patroon dat enkel uit onzichtbare tekens bestaat, een reguliere expressie die niet compileert of catastrofaal terugkeert, een patroon dat te kort is en geen punt bevat om op een URL te mikken, of een categorie die niet meer op de site bestaat. Corrigeer of verwijder die regels: ze geven een valse indruk van dekking.


FAQ

Werkt de plugin achter een paginacache? Ja, en haar volledige architectuur is rond die beperking gebouwd. De geproduceerde HTML is voor alle bezoekers identiek; de toestemming wordt in de browser gelezen en toegepast vóór de eerste render. Er wordt geen enkele nonce in cachebare HTML afgedrukt. Alleen de antwoorden die werkelijk persoonlijk zijn, worden als niet-cachebaar gemarkeerd.

Vervangt ze een betalende CMP? Op de meeste sites dekt ze hetzelfde terrein: banner, blokkering, auditlogboek, rechtenverzoeken, geografische routering en gegenereerde documenten. Twee dingen doet ze niet: het is geen bij IAB Europe geregistreerde CMP, en ze levert geen juridisch advies en evenmin nagelezen documenten — de generator produceert concepten die u moet laten valideren.

Is het register onvervalsbaar? Het is aantoonbaar bij manipulatie, onder één voorwaarde. Elke regel is met HMAC ondertekend over de hash van de vorige: een gewijzigde of verwijderde regel breekt de keten en de verificatie zegt waar. De garantie steunt erop dat de ondertekeningssleutel buiten de database leeft. Dat is zo als AUTH_KEY en AUTH_SALT in wp-config.php staan, of als u OWC_LEDGER_KEY definieert. Anders bewaart WordPress de salts in de database en zou een aanvaller die daar binnengeraakt de keten opnieuw kunnen ondertekenen — de plugin detecteert die situatie en waarschuwt u.

Vertraagt de scanner de site? Nee. Hij staat standaard uit, en eenmaal ingeschakeld draait hij op WP-Cron met de frequentie die u kiest, waarbij hij aan de serverzijde een steekproef van uw eigen pagina's ophaalt. Hij draait nooit terwijl een bezoeker de site bekijkt.

Vindt de scanner de cookies? Gedeeltelijk, en dat is belangrijk om te begrijpen. De serverscan leest de Set-Cookie-headers van uw pagina's: hij vindt dus de cookies die de server plaatst, inclusief de HttpOnly-cookies. Hij voert geen JavaScript uit, dus de cookies die scripts in de browser schrijven zijn voor hem onzichtbaar; die worden opgehaald door een probe die alleen voor een ingelogde beheerder draait. Behandel het resultaat als een inventaris van wat er is gezien, niet als een volledige lijst — beide schermen zeggen dat, in plaats van u het tegendeel te laten veronderstellen.

Hoe wordt een rechtenverzoek uitgevoerd? De persoon dient het formulier in en krijgt een verificatiemail. Het verzoek bevestigen op de gelinkte pagina start de termijn van artikel 12.3 en opent een native WordPress-verzoek. Vanuit het DSAR-scherm downloadt u een JSON-export die door alle op de site geregistreerde exporters is geproduceerd, start u alle erasers, en sluit u af met een geschreven antwoord dat aan het verzoek gekoppeld blijft. Wat de export dekt hangt dus af van de geïnstalleerde plugins; die geen van deze hooks registreren, moet u met de hand behandelen.

Kan ik de TCF-module gebruiken voor AdSense of Ad Manager? Alleen met uw eigen CMP ID van IAB Europe, en zelfs dan met een voorbehoud. Zonder CMP ID laadt de module helemaal niet. Met een CMP ID stuurt ze een correct gecodeerde TC-string uit en toont het paneel elk doel en elke speciale functie die die string kan claimen — maar het TCF-beleid eist van een geregistreerde CMP keuzes op doelniveau én op vendorniveau. Hier volgen de doelschakelaars de categorieën en is er geen enkele keuze op vendorniveau: dit is geen geregistreerde CMP, en vendors mogen haar signaal terecht weigeren. Als advertentie-inkomsten onder TCF voor uw site meetellen, gebruik dan een gecertificeerde CMP.

In welke taal worden de documenten gegenereerd? Het privacybeleid en het cookiebeleid bestaan voor de elf profielen, in de taal van het rechtsgebied: Engels, Frans voor Frankrijk, België, Luxemburg en Quebec, Portugees voor Brazilië. De wettelijke vermeldingen en de algemene voorwaarden bestaan enkel in het Frans; voor elk ander rechtsgebied weigert de generator ze te produceren in plaats van een ongeschikt document te publiceren.

Wat kan ik aan de banner veranderen? Vier posities, een licht, donker of automatisch thema, een optioneel logo, de labels, en een zwevende knop om hem opnieuw te openen. Elk element met de klasse owc-open-preferences opent het paneel opnieuw. “Alles weigeren” wordt met dezelfde prominentie gerenderd als “Alles accepteren”. Het sluitkruisje staat standaard uit; staat het aan, dan registreert het een volledige weigering, nooit een stille sluiting. Het paneel is met het toetsenbord bruikbaar, de schakelaars dragen een zichtbare status, en de modal vangt de focus en geeft die daarna terug. De teksten zijn vertaalbaar en vervangbaar via het filter owc_banner_texts.

Wat blokkeert de automatische blokkering precies? Scripts van derden, inline trackingsnippets, iframes, meetpixels, analytics- en marketingstylesheets en mediabronnen van derden worden vóór de toestemming herschreven en daarna vrijgegeven, in documentvolgorde. Resource hints naar een gecatalogiseerde derde worden verwijderd in plaats van uitgesteld, omdat een hint een verbinding opent en er niets valt te herstellen. Stylesheets en lettertypes die door een als functioneel ingedeelde host worden geserveerd, blijven onaangeroerd. De lazy-loadattributen van cacheplugins worden onschadelijk gemaakt zodat een loader geen geblokkeerde URL herstelt. De plugin levert 175 signaturen mee, aanpasbaar via het scherm Tracker catalogue en uitbreidbaar via een filter.

Wat wordt er in het register geschreven? Elke toestemmingsactie — acceptatie, weigering, gedeeltelijke registratie, GPC-opt-out, vernieuwing, intrekking — wordt aan de geketende tabel toegevoegd. Een regel bevat een gepseudonimiseerd IP-adres, een hash van de user-agent, de pagina-URL, het compliance-profiel, een hash van de geldende documenten, een hash van de banner die werkelijk is getoond en de versie van de plugin — de elementen waarmee u kunt reconstrueren wat de bezoeker heeft gezien.

Welke beveiligingen zitten er op het rechtenformulier? Een honeypot, een minimale invultijd, een verplichte verklaring die aan de serverzijde wordt gecontroleerd, drie afzonderlijke rate limits, een eenmalig token dat alleen als hash wordt opgeslagen, en een bevestiging die een uitdrukkelijke actie op de pagina vereist — zodat een linkscanner van een mailsysteem geen identiteit kan bevestigen in de plaats van de persoon. Een verzoek waarvan de indiener zijn identiteit nooit heeft bevestigd, kan niet worden geëxporteerd, niet worden gewist en niet als ingewilligd worden genoteerd, en die controle gebeurt aan de serverzijde, niet enkel door knoppen te verbergen.

Welke signalen van Google Consent Mode v2 worden uitgestuurd? Alle zeven: ad_storage, ad_user_data, ad_personalization, analytics_storage, functionality_storage, personalization_storage en security_storage. De zes categorieën zijn eraan gekoppeld vanuit één enkele bron van waarheid, zodat de banner, de blokkering en de bootstrap niet uit elkaar kunnen lopen.

Hoe worden de CCPA-opt-out en GPC behandeld? Een besturingselement “Do Not Sell or Share” wordt in de footer geïnjecteerd of via shortcode geplaatst. De eerste klik registreert de opt-out, zoals de Californische regels vereisen, in plaats van een paneel te openen. Het Global Privacy Control-signaal wordt onder de Amerikaanse profielen als een bindende opt-out behandeld, één keer per surfsessie, en de bezoeker wordt verwittigd als het een keuze heeft vervangen die hij had geregistreerd. Onder de profielen van de AVG-familie wordt GPC als een aanwijzing behandeld: de optionele categorieën staan in de interface al op geweigerd, de banner blijft zichtbaar, en er wordt niets geregistreerd — omdat de toestemming daar een positieve handeling moet zijn.

Welke cookies plaatst de plugin zelf? Het zijn allemaal interne cookies, geen enkele dient voor tracking, en ze worden opgesomd in het gegenereerde cookiebeleid: owc_consent (de keuzes, het pseudonieme token van de browser en het profiel; duur volgens de vernieuwingsinstelling, standaard 12 maanden, afgetopt op 13), owc_geo (gedetecteerd land en profiel, 24 uur, zonder enig IP-adres), owc_gpc (sessiemarkering), owc_gpc_notice (5 minuten), en euconsent-v2 (TCF-string, alleen als de module aan staat en na een handeling van de bezoeker).

Werkt de plugin in multisite? Ja. Elke site van het netwerk heeft haar eigen tabellen en haar eigen instellingen. Een netwerkactivatie loopt alle sites af alleen als het netwerk er hoogstens 200 telt; daarboven wordt elke site bij haar eerste request geprovisioneerd.

En als ik de plugin verwijder? Het register, de vondsten van de scanner, de catalogus, de OW Forms-koppelingen, de opties en de instellingen blijven standaard bestaan. Twee dingen verdwijnen altijd, wat u ook hebt ingesteld: de zes geplande taken, en de tabel met de rechtenverzoeken — dat is de enige tabel met direct identificerende persoonsgegevens van derden, en zodra de plugin weg is, begrenst niets nog de bewaring ervan en is er geen scherm meer om erop te antwoorden. Exporteer uw verzoeken vóór u de plugin verwijdert. Voor een volledige opkuis van de rest zet u “Delete all data on uninstall” aan vóór het verwijderen.

Waar vind ik support?