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
- Overzicht
- Installatie
- Snelstart
- De elf compliance-profielen
- De cookiecategorieën
- De banner
- De automatische blokkering
- De trackerscanner
- De generator voor juridische documenten
- Het toestemmingsregister
- Het rechtenportaal (DSAR)
- De CCPA-opt-out “Do Not Sell or Share”
- Google Consent Mode v2
- IAB TCF v2.2
- Global Privacy Control
- De regiodetectie
- De OW Forms-integratie
- De zwevende knop
- Shortcodes
- REST API
- Instellingenreferentie
- Probleemoplossing
- 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 krijgtquebec. - 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
- Download
ow-consent-1.4.3.zipvan https://optionweb.dev/nl/addons/ow-consent/ - Plugins → Nieuwe plugin → Plugin uploaden
- 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 vanwpdb::prepare(), die WordPress pas vanaf 6.2 begrijpt. Op een oudere core zouprepare()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 headerRequires at leastverhindert 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:
| Tabel | Inhoud |
|---|---|
{prefix}owc_ledger | Het toestemmingsregister, geketend via hashes |
{prefix}owc_dsar | De verzoeken van betrokkenen |
{prefix}owc_scanner | De vondsten van de trackerscanner |
{prefix}owc_scripts | De 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 metINSERT 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_upgradeingepland.
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:
- Altijd, wat u ook hebt ingesteld: de zes crons worden gewist en de tabel
{prefix}owc_dsarwordt 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. - Alleen als
delete_data_on_uninstalluitdrukkelijk aan staat: verwijdering van de tabellenowc_ledger,owc_dsar,owc_scanner,owc_scriptsenowc_form_links, van de benoemde opties, van alle opties met het voorvoegselowc_(transients inbegrepen) en van deusermetamet het voorvoegselowc_. 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
| Profiel | Beoogd regime | Model |
|---|---|---|
gdpr | AVG + ePrivacy (EU/EER) | Opt-in |
uk_pecr | UK GDPR + PECR (Verenigd Koninkrijk) | Opt-in |
ch_nfadp | Zwitserse nFADP | Opt-in |
quebec | Wet 25 (Quebec) | Opt-in |
lgpd | LGPD (Brazilië) | Opt-in |
popia | POPIA (Zuid-Afrika) | Opt-in |
pipl | PIPL (China) | Opt-in |
dpdp | DPDP Act 2023 (India) | Opt-in |
ccpa | CCPA / CPRA (Californië) | Opt-out |
us_generic | Algemene Amerikaanse staatswetten | Opt-out |
au | Privacy 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 verandert | Detail |
|---|---|
| Toestemmingsmodel | Opt-in, behalve ccpa, us_generic, au |
| Standaardwaarden van Consent Mode | Onder een opt-outprofiel gaan de zeven signalen op granted |
| Verplichte link “Do Not Sell or Share” | Alleen ccpa en us_generic |
| Juridisch bindende GPC | Alleen ccpa en us_generic — Australië is uitdrukkelijk uitgesloten: opt-outregime, maar het erkent GPC niet |
| Bereik “de AVG is van toepassing” voor TCF | 27 EU-landen + IS, LI, NO + GB + CH, samen 31 codes |
| Taal van het gegenereerde document | quebec → Frans; lgpd → Portugees; gdpr met land FR, BE of LU → Frans; al de rest → Engels |
| Vermelde toezichthoudende autoriteit | Tabel per profiel, voor de AVG verfijnd per land |
| Gepubliceerde lijst van rechten | Een per profiel geschreven lijst, met artikelverwijzing |
| Blok “cookieregime” van het Engelse document | Tekst, 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
| Profiel | Genoemde autoriteit |
|---|---|
gdpr, land FR | CNIL |
gdpr, land BE | GBA-APD |
gdpr, land LU | CNPD |
gdpr, land DE | BfDI |
gdpr, ander EER-land | Algemene formulering (“de bevoegde toezichthoudende autoriteit”) |
uk_pecr | ICO |
ch_nfadp | EDÖB / FDPIC |
quebec | Commission d'accès à l'information |
lgpd | ANPD |
ccpa | California Privacy Protection Agency |
popia | Information Regulator (South Africa) |
pipl | Cyberspace Administration of China |
dpdp | Data Protection Board of India |
au | OAIC |
us_generic | De 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_genericbestaat 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:
| Slug | Meegeleverd label | Meegeleverde beschrijving |
|---|---|---|
necessary | Necessary | Strictly required for the site to function (cart, login, language preferences). Cannot be disabled. |
functional | Functional | Enhance the experience (chat, embedded videos, maps). Without them some features may not work. |
analytics | Statistics | Help us understand how you use the site (anonymously). No personal data is shared for commercial purposes. |
marketing | Marketing | Enable us to show you ads and content tailored to your interests on other sites. |
preferences | Preferences | Remember your interface choices (layout, saved filters). |
social | Social & embeds | Allow 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.
| Categorie | Signalen van Consent Mode v2 |
|---|---|
necessary | security_storage, functionality_storage |
functional | functionality_storage, personalization_storage |
analytics | analytics_storage |
marketing | ad_storage, ad_user_data, ad_personalization |
preferences | personalization_storage |
social | ad_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
functional → functional, analytics → measurement, marketing → advertising,
preferences → personalization, social → social_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
| Instelling | Waarden | Standaard |
|---|---|---|
banner_position | bottom-bar, bottom-card, center-modal, top-bar | bottom-bar |
banner_style | auto, light, dark | auto |
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
| Instelling | Standaard | Effect |
|---|---|---|
banner_accept_all | true | Toont “Alles accepteren” |
banner_reject_all | true | Toont “Alles weigeren” |
banner_preferences | true | Toont “Aanpassen” |
banner_close_x | false | Toont het sluitkruisje |
banner_show_logo | true | Toont 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:
- Staat
banner_close_xuit, dan zit het kruisje wel in de markup maar draagt het het attribuuthidden, en haalt de CSS het volledig weg: niet zichtbaar, niet focusbaar, niet aangekondigd aan schermlezers. - Staat het aan, dan voert een klik op het kruisje het volledige pad “Alles weigeren” uit, nooit een stille sluiting.
- 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 eenwp_ksesdie 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">metaria-checked,aria-labelledbyenaria-describedby. De status wordt gedragen dooraria-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
stickyonderaan, 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
| Instelling | Standaard | Grenzen |
|---|---|---|
consent_renewal_months | 12 | 0 tot 13 |
consent_policy_hash_check | true | — |
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.
- 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
Hostherschrijft), dan is die URL cross-origin en weigert de browser deSet-Cookieop te slaan terwijl WordPress netjes 200 antwoordt. De runtime verankert het pad dat PHP aanlevert daarom opnieuw op de origin die werkelijk wordt bezocht. - Nonce. Vlak vóór de schrijfactie vers opgehaald via
GET /owc/v1/nonce, nooit ingebakken in cachebare HTML, verstuurd in de headerX-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. - Expliciete
page_urlin de body van het request, zodat de registerregel niet afhangt van de headerReferer, die een extensie, een meta-referrer of een proxy kan weghalen. - Blokkeerbeveiliging van 15 seconden met
AbortController: het request wordt afgebroken, niet alleen genegeerd, zodat een late POST geen tweede regel schrijft. - Definitie van succes. De POST is alleen geslaagd als: het antwoord
okis, 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. - 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. - Eén enkele nieuwe poging, en alleen bij een 403 met de code
owc_bad_nonce,rest_cookie_invalid_nonceofrest_nonce_invalid, en alleen als de verkregen nonce werkelijk verschilt. - 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 bijhashchangewordt opgepikt; - elk element met de klasse
owc-open-preferencesof het attribuutdata-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()enwp_footer(): een thema dat ze niet aanroept, krijgt niets. - Zonder
window.fetchenwindow.Promiseis geen enkele schrijfactie mogelijk en wordt de foutbrowsergetoond. - 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_tagis aangehaakt. Met andere woorden, alleen de viawp_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 zelfContent-Type: text/htmlheeft 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:
- al behandeld → onaangeroerd;
- script van de plugin zelf → onaangeroerd;
type="text/plain"of een niet-JS-type (ld+json,importmap,x-template) → onaangeroerd;- 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; - host op de allowlist → onaangeroerd; first-party bron die niet op een tracker lijkt → onaangeroerd;
- vergelijking met de catalogus op de vorm
host + pad; eendata:- ofjavascript:-URL wordt gedecodeerd en als inline code beoordeeld; - een regel die als
necessaryis ingedeeld wordt nooit geblokkeerd (Stripe.js, reCAPTCHA, Turnstile, cdnjs …): ze blokkeren levert geen enkele compliance op en breekt het aanroepende snippet; - geen enkele treffer →
blocker_unknown_script_policy, standaardallow; - anders herschrijven naar
type="text/plain"metdata-owc-cat,data-owc-vendor,data-owc-srcen, als het oorspronkelijke type bijzonder was (modulebijvoorbeeld),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
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
analyticsofmarketingis ingedeeld, wordt onschadelijk gemaakt. - Google Fonts en de andere lettertypehosts die als
functionalzijn 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_consentwordt 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.srcensetAttributeworden ingepakt. Een first-party loader dies.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
onloadofonerror, met een maximum van 5 seconden zodat een onbereikbare leverancier de rest niet ophoudt. Het attribuutasyncwordt alleen toegepast als het oorspronkelijk aanwezig was — zonder dat zou een script dat viacreateElementwordt gemaakt asynchroon worden en zou het configuratiesnippet vóór zijn bibliotheek draaien. Voor een hersteld inline script wordtdocument.writetijdelijk 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
MutationObserverscant 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:
| Verdeling | Detail |
|---|---|
| Per doel | 110 URL-patronen, 65 cookienamen |
| Per type | 172 tekstfragmenten, 3 reguliere expressies |
| Per categorie | necessary 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
hostdat 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
| Instelling | Standaard | Waarden |
|---|---|---|
compliance_strict | true | boolean |
blocker_unknown_script_policy | allow | allow, block |
blocker_unknown_iframe_policy | block | allow, block |
blocker_block_resource_hints | true | boolean |
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
- Zonder
compliance_strictworden alleen de door WordPress in de wachtrij gezette scripts behandeld. - Een onbekend script van derden wordt standaard toegelaten. De echte blokkering steunt op de catalogus en de inline signaturen — vul ze aan.
- 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.
- Stylesheets die als
functionalzijn ingedeeld (webfonts) worden bewust doorgelaten. - Een tracker die als
<img>in de catalogus staat maar anders dananalyticsofmarketingis ingedeeld, wordt niet onschadelijk gemaakt. - Media (
object,embed,source,video,audio) worden nooit standaard geblokkeerd: alleen een reeds gecatalogiseerde derde wordt behandeld. - 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.
- 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
| Beperking | Waarde |
|---|---|
| URL's per uitvoering | scanner_max_urls, standaard 25, grenzen 1 tot 500 |
| Time-out per request | scanner_timeout, standaard 8 s, grenzen 1 tot 60 |
| Kloktijdbudget per batch | max_execution_time − 10 s, anders 45 s, begrensd tussen 5 en 60 s |
| Uitvoeringslock | 15 minuten |
| Levensduur van de wachtrij | 6 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
cookiewordt een regelmatch_target = cookieop de naam van de cookie; - een vondst van het type
inline_scriptschrijft 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:
- Niet-gecategoriseerde trackers — alleen verstuurd voor werkelijk nieuwe identificatoren, met een geheugen dat op 500 entries is afgetopt.
- “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
| Instelling | Standaard | Grenzen |
|---|---|---|
scanner_enabled | false | boolean |
scanner_frequency | weekly | hourly, twicedaily, daily, weekly |
scanner_max_urls | 25 | 1 tot 500 |
scanner_timeout | 8 | 1 tot 60 |
scanner_probe_mode | admins | admins, off |
scanner_alert_email | '' | e-mailadres |
Goed om te weten:
scanner_probe_modewordt 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 opscanner_enabled.offopslaan bewaart de waarde zonder de probe uit te schakelen. Om de probe echt te stoppen, schakelt u de scanner uit.
Grenzen van de scanner
- De serverscan voert geen enkel stukje JavaScript uit. Zonder de probe ontbreekt in de inventaris wat een tagmanager injecteert.
- 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.
- Een cookie die via een
Set-Cookie-header wordt geplaatst, wordt gedetecteerd maar is niet blokkeerbaar. - De scan verlaat nooit het domein en is standaard op 25 URL's afgetopt: een grote site wordt nooit volledig gedekt.
- Op een host waar de HTTP-loopback is geblokkeerd (HTTP-authenticatie op een staging-omgeving, firewall) leest de scan niets.
- Een vondst die 90 dagen niet meer is waargenomen verdwijnt, en verdwijnt dus ook uit het cookiebeleid.
- De scanner hangt af van WP-Cron: op een site met
DISABLE_WP_CRONen zonder systeemcron gaat de geplande scan niet af.
De generator voor juridische documenten
De vier documenten
| Type | Inhoud | Beschikbare talen |
|---|---|---|
cookie_policy | Cookiebeleid | Frans, Engels, Portugees |
privacy_policy | Privacybeleid | Frans, Engels, Portugees |
legal_notice | Wettelijke vermeldingen | alleen Frans |
terms | Algemene voorwaarden | alleen 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.
| Document | Vereiste velden |
|---|---|
| Alle | legal_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
| Profiel | Taal van het document |
|---|---|
quebec | Frans |
lgpd | Portugees |
gdpr met legal_country ∈ {FR, BE, LU} | Frans |
| Al de rest | Engels |
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.
- 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.
- 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.
- Niet-ingedeelde trackers worden niet verstopt: ze krijgen hun eigen sectie. Dat zijn de trackers die niemand heeft bekeken.
- 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. - 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 instellingen —
ledger_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
| Methode | Pad | Parameters | Toegang |
|---|---|---|---|
POST | /owc/v1/policies/generate | type (vereist), publish (boolean, standaard false) | manage_options |
GET | /owc/v1/policies/preview | type (vereist) | manage_options |
De HTML van de preview gaat door wp_kses_post() vóór ze wordt teruggegeven.
Grenzen van de generator
- Dit zijn modellen, geen juridisch advies. Laat ze nalezen.
- Wettelijke vermeldingen en algemene voorwaarden bestaan alleen in het Frans. Elk ander rechtsgebied botst op het taalslot, tenzij u uw eigen tekst aanlevert.
- Slechts drie talen: Frans, Engels, Portugees.
- Vier EER-landen hebben een genoemde autoriteit; elders een algemene formulering.
- De trackertabel is niet meer waard dan uw scan — en het document zegt dat ook.
- 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:
| Kolom | Type | Inhoud |
|---|---|---|
id | bigint | Primaire sleutel |
created_at | datetime | UTC-tijdstempel |
visitor_token | char(32) | Pseudoniem token van de browser, 32 hexadecimale tekens |
event | varchar(20) | accept_all, reject_all, save_preferences, withdraw, auto, gpc_opt_out |
categories | varchar(255) | Lijst van de toegestane categorieën |
profile | varchar(20) | Compliance-profiel dat gold op het moment van de actie |
source | varchar(60) | banner, preferences, footer_link, api, auto |
ip_pseudonymous | varchar(45) | Ingekort IP-adres |
ua_hash | char(64) | Gesalte hash van de user-agent |
page_url | varchar(500) | Pagina waar de actie plaatsvond |
prev_hash | varchar(128) | Hash van de vorige regel |
row_hash | varchar(128) | Hash van deze regel |
policies_hash | char(64) | Hash van de geldende documenten |
banner_revision | varchar(40) | Hash van de banner die werkelijk is getoond |
plugin_version | varchar(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:
| Herkomst | Voorwaarde | Oordeel |
|---|---|---|
constant | OWC_LEDGER_KEY is gedefinieerd | Onvervalsbaar |
wp-config | AUTH_KEY en AUTH_SALT zijn gedefinieerd, niet leeg, onderling verschillend en zonder de standaardzin | Onvervalsbaar |
database | Anders | Niet 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; deRefererwordt 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
- De onvervalsbaarheid is voorwaardelijk, en de plugin zegt dat zelf.
- 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”.
- Het opschonen is afgetopt op 20 000 regels per dagelijkse uitvoering.
- 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:
| Attribuut | Standaard | Rol |
|---|---|---|
types | access,rectification,erasure,portability,restrict,object,optout | Aangeboden 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:
accessenportability→ export;erasure→ verwijdering; de andere types openen geen native verzoek. - De verantwoordelijke wordt verwittigd op
dsar_emailendsar_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.
| Actie | Gedrag |
|---|---|
| Gegevens downloaden (JSON) | Weigering met 409 als de identiteit nooit is bevestigd. Het bestand heet dsar-<id>-<JJJJMMDD>.json |
| Gegevens wissen | Alleen 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 maanden | Verlenging 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 |
| Afsluiten | Status 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 versturen | Geeft 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:
- inzendingen die nooit zijn bevestigd worden verwijderd — geen enkele bewijswaarde, alleen persoonsgegevens;
- 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; - een verzoek dat nog openstaat wordt bewaard, met een bericht dat uitlegt dat het moet worden beantwoord vóór het mag worden verwijderd;
- 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
| Instelling | Standaard | Grenzen |
|---|---|---|
dsar_enabled | true | boolean |
dsar_email | '' | publiek contact en ontvanger van de meldingen |
dsar_notify_email | '' | bijkomende ontvanger |
dsar_response_days | 30 | 1 tot 30 — nooit meer dan een maand |
dsar_token_ttl_days | 7 | 1 tot 90 |
retention_dsar_days | 1095 (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
- 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.
- Een mislukte e-mailverzending blokkeert de cyclus: het antwoord toont
mail_sent: falseen het formulier toont een bericht dat naar het contactadres verwijst, maar het verzoek blijftpendingen de wettelijke termijn start niet. - De termijnherinneringen en het opruimen hangen af van WP-Cron.
- 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"]
| Attribuut | Standaard |
|---|---|
label | “Do Not Sell or Share My Personal Information” |
class | owc-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
- Het besturingselement met één klik vereist
fetchenPromise. - De link is alleen verplicht onder de profielen
ccpaenus_generic; onder elk ander profiel wordt hij niet getoond en niet zichtbaar gemaakt. - 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.
| Signaal | Opt-inregime | Opt-outregime |
|---|---|---|
ad_storage | denied | granted |
analytics_storage | denied | granted |
ad_user_data | denied | granted |
ad_personalization | denied | granted |
personalization_storage | denied | granted |
functionality_storage | denied als de site de categorie functional toont, anders granted | granted |
security_storage | altijd granted | granted |
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
| Instelling | Standaard | Effect |
|---|---|---|
gcm_ads_data_redaction | true | Maskeert de advertentie-identificatoren zolang ad_storage geweigerd is |
gcm_url_passthrough | true | Laat 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
| Instelling | Standaard | Grenzen |
|---|---|---|
tcf_enabled | false | boolean |
tcf_cmp_id | 0 | 0 tot 4095 |
tcf_publisher_country | FR | ISO-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
- Geen CMP ID (
tcf_cmp_id < 1): geen__tcfapi, geen TC-string, geen cookieeuconsent-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. - 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.
- 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 eenETagen eenCache-Control: public, max-age=86400, en behandelt conditionele requests. Zit er niets in de cache, dan antwoordt ze 503 met eenRetry-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
| Categorie | Doelen |
|---|---|
necessary | geen — buiten het TCF-bereik, wat garandeert dat “Alles weigeren” geen toestemming voor doel 1 kan opleveren |
functional | 1 |
analytics | 1, 8, 9, 10 |
marketing | 1, 2, 3, 4, 7 |
preferences | 1, 5, 6, 11 |
social | 1 |
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
- 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.
- Alleen het Core-segment: geen segment
disclosedVendors,allowedVendorsofpublisherTC. - Er wordt geen enkele publisher restriction uitgestuurd: de bijhorende teller staat altijd op 0 en het object met de restricties is leeg.
purposeOneTreatmentenuseNonStandardTextsstaan altijd opfalse,isServiceSpecificstaat altijd optrue— er is geen globale scope — en het toestemmingsscherm is altijd 0.getVendorListnegeert de versieparameter en geeft altijd de gecachete lijst terug.- De toestemmingen van de uitgever kopiëren de algemene doelen: geen aangepaste doelen.
- Slechts twee speciale functies.
- 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:
gpc_honorstaat aan;- het signaal is aanwezig — de header
Sec-GPC, met terugval opX-Sec-GPComdat sommige proxy's en CDN's de header hernoemen; alleen de exacte waarde1telt; - het effectieve profiel is
ccpaofus_generic— anders wordt er niets geschreven; - het is een gewone paginaweergave: geen beheer, geen cron, geen AJAX, geen REST, geen XML-RPC,
geen WP-CLI, en de methode is
GET; - de headers zijn nog niet verstuurd — een cookie die u niet kunt plaatsen, is een beslissing die u bij elk request opnieuw zou registreren;
- de sessiemarkering
owc_gpcontbreekt: één keer per surfsessie; - 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,libwwwoffacebookexternalhitbevat); - de al opgeslagen keuze voldoet nog niet aan het signaal — anders wordt alleen de browser gemarkeerd;
- de gedeelde rate limit wordt gerespecteerd: 30 per uur en per IP;
- 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_personalizationenpersonalization_storageopdenied; - alle optionele categorieën gaan op
false, enkel in het geheugen; givenblijft 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
| Cookie | Duur | Rol |
|---|---|---|
owc_gpc | sessie | Markeert dat het signaal voor deze browser al is behandeld |
owc_gpc_notice | 300 s | Zet 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.
| Instelling | Standaard |
|---|---|
geo_enabled | false |
geo_default_profile | gdpr |
geo_mmdb_enabled | false |
De detectiecascade
De eerste treffer wint:
- Cloudflare (
CF-IPCountry) — alleen als de site heeft verklaard achter Cloudflare te draaien; - AWS CloudFront (
CloudFront-Viewer-Country) — dezelfde voorwaarde; 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;- Lokale MaxMind-database, als
geo_mmdb_enabledaan staat; - 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,Securebij 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
| Land | Profiel |
|---|---|
| De 30 EER-landen | gdpr |
| GB, JE, GG, IM | uk_pecr |
| CH | ch_nfadp |
| BR | lgpd |
| ZA | popia |
| CN, HK | pipl |
| IN | dpdp |
| AU, NZ | au |
| CA | quebec |
| US | ccpa |
| Al de rest | De 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_countryenrepresented_countryworden 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
- Standaard uit, en de MaxMind-variant vereist een bestand dat u zelf aanlevert.
- Landheaders worden genegeerd zolang u uw proxy niet hebt verklaard.
- Geen onderscheid per staat in de Verenigde Staten, en evenmin per provincie in Canada.
- Het regime van de bezoeker hangt af van JavaScript en van de cookie van 24 uur.
- Het profiel
us_genericwordt 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
- 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.
- 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.
- Geen enkele retroactieve inhaalslag: de koppeling begint bij de eerste inzending na de update.
- 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.
- De integratie wijzigt nooit de plugin OW Forms, haar tabellen of haar opties.
- 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
| Instelling | Waarden | Standaard |
|---|---|---|
floating_button_enabled | boolean | true |
floating_button_position | bottom-left, bottom-right, top-left, top-right | bottom-left |
floating_button_style | pill, icon | pill |
floating_button_label | vrije tekst | Cookies |
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"]
| Attribuut | Standaard |
|---|---|
types | access,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"]
| Attribuut | Standaard |
|---|---|
label | “Do Not Sell or Share My Personal Information” |
class | owc-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.
| Methode | Pad | Parameters | Rol |
|---|---|---|---|
GET | /nonce | action (standaard wp_rest) | Geeft een vers token terug. Antwoord: {nonce, action, header: "X-OWC-Nonce", ttl: 43200}, met no-store en Vary: Cookie |
POST | /consent | event (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 | /state | — | De 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/gvl | — | Serveert 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 | /dsar | type (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_outsource:banner,preferences,footer_link,api,autotype(DSAR):access,rectification,erasure,portability,restrict,object,optout,withdrawcategories: een object, maximaal 32 entries, uitsluitend scalaire waardenaction(nonce): uitsluitendwp_rest
Beheerroutes
Alle vereisen de capability manage_options.
| Methode | Pad | Parameters | Rol |
|---|---|---|---|
GET | /ledger | page (≥1, standaard 1), per_page (1–200, standaard 50), visitor_token (32 hex), from, to | Leest het register. De filters die werkelijk zijn toegepast, komen mee in het antwoord |
GET | /ledger/verify | — | Controleert de keten van begin tot eind |
GET | /settings | — | De effectieve instellingen |
POST | /settings | vrije JSON-body | Schrijft de instellingen. Antwoord {ok, updated, rejected, settings} |
POST | /scanner/run | — | Start een scan |
POST | /scanner/ingest | JSON-body {page, findings[]} | Ontvangst van de waarnemingen van de probe |
GET | /scanner/findings | page, per_page (1–200, standaard 50), filter (categorieslug of unknown) | Toont de vondsten |
POST | /policies/generate | type (vereist), publish (boolean, standaard false) | Genereert een document |
GET | /policies/preview | type (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.
- Origincontrole op dezelfde host. Gratis, deterministisch, verbruikt geen enkel budget.
- Een plugineigen token in de header
X-OWC-Nonce— dat is een bewijs, nooit een veto. De keuze omX-WP-Nonceniet 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. - Falen gesloten: geen origin en geen geverifieerd token geven een 403
owc_missing_origin. - 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
| Bucket | Budget | Venster | Sleutel |
|---|---|---|---|
nonce | 120 | 5 minuten | IP-adres |
consent | 30 | 5 minuten | IP-adres |
dsar | 3 | 1 uur | IP-adres |
dsar_email | 3 | 24 uur | beoogd e-mailadres |
dsar_global | 30 | 1 uur | de hele site |
gpc | 30 | 1 uur | IP-adres |
tcf_gvl | 10 | 1 uur | IP-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_PROXYis 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 )—0teruggeven 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, metcookie_set: falseen een descriptorcookie(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_urlwordt gevalideerd tegen de hosts van de site, met terugval op eenRefererdie op dezelfde manier is gevalideerd, en anders op de lege string. De sleutel is altijd aanwezig, zodat het register niet zelf de ruweReferergaat 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_unreadableis 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
| Code | HTTP | Betekenis |
|---|---|---|
owc_bad_param | 400 | Ongeldige parameter |
owc_bad_categories | 400 | Object categories misvormd, te groot of niet-scalair |
owc_bad_email | 400 | Ongeldig e-mailadres |
owc_bad_dsar | 400 | Misvormde DSAR-inzending (honeypot inbegrepen) |
owc_dsar_not_attested | 400 | Verklaring uit art. 12.6 ontbreekt |
owc_no_settings | 400 | Geen enkele bekende instellingssleutel in de body |
owc_missing_origin | 403 | Geen origin en geen token: falen gesloten |
owc_bad_origin | 403 | De opgegeven origin is geen host van deze site — het antwoord somt tot tien aanvaarde hosts op |
owc_bad_nonce | 403 | Ongeldige nonce op /scanner/ingest |
owc_jsonp_forbidden | 403 | JSONP-vorm geweigerd op /nonce |
owc_forbidden | 401/403 | Onvoldoende capability |
owc_dsar_disabled | 404 | Het rechtenportaal staat aan de serverzijde uit |
owc_no_template | 404 | Geen sjabloon voor dit type en dit profiel |
owc_rate_limited | 429 | Rate limit bereikt |
owc_consent_failed | 500 | Toepassen van de toestemming mislukt |
owc_dsar_store_failed | 500 | Wegschrijven van het verzoek mislukt |
owc_gen_failed | wisselend | Documentgeneratie geweigerd (ontbrekende velden, taalslot) |
owc_ledger_write_refused | 503 | Het 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
/consenten/dsaraanvaarden anonieme schrijfacties, by design; de bescherming is de origin plus de rate limit, niet een nonce.- De rate limiter faalt open zonder objectcache.
- 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. /scanner/ingestaanvaardt alleenmanage_options: de probe kan niet van een bezoeker komen./settingsheeft geen eigen nonce: het ismanage_optionsplus 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.
| Instelling | Aanvaarde waarden |
|---|---|
banner_position | bottom-bar, bottom-card, center-modal, top-bar |
banner_style | auto, light, dark |
floating_button_position | bottom-left, bottom-right, top-left, top-right |
floating_button_style | icon, pill |
compliance_profile, geo_default_profile | gdpr, ccpa, lgpd, popia, pipl, dpdp, quebec, uk_pecr, ch_nfadp, au, us_generic |
ledger_hash_algo | sha256, sha3-256 (doorsneden met wat PHP ondersteunt) |
scanner_frequency | hourly, twicedaily, daily, weekly |
scanner_probe_mode | admins, off |
legal_business_type | auto, vitrine, rental, ecommerce, services, saas, content |
blocker_unknown_script_policy, blocker_unknown_iframe_policy | allow, block |
Grenzen van de gehele getallen
| Instelling | Grenzen |
|---|---|
consent_renewal_months | 0 tot 13 (0 wordt als 13 behandeld) |
dsar_response_days | 1 tot 30 |
dsar_token_ttl_days | 1 tot 90 |
ledger_retention_days | 0 tot 3650 (0 = onbeperkt) |
scanner_max_urls | 1 tot 500 |
scanner_timeout | 1 tot 60 |
legal_minor_age | 13 tot 18 |
retention_form_data_days | 1 tot 3650 |
retention_dsar_days | 1 tot 3650 |
tcf_cmp_id | 0 tot 4095 |
Opschoning
text_messageis 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_desceindigt. - URL's: elke sleutel met het voorvoegsel
link_of het achtervoegsel_url, pluslegal_profession_rules. - E-mailadressen: elke sleutel die
emailbevat. - 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
| Tabblad | Instellingen |
|---|---|
| Banner | banner_*, text_*, floating_button_*, consent_renewal_months, consent_policy_hash_check |
| Compliance | compliance_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 identity | legal_*, retention_form_data_days, retention_dsar_days, policy_disclaimer |
| Categories | cat_*_available, cat_*_label, cat_*_desc |
| Audit ledger | ledger_enabled, ledger_retention_days (het algoritme wordt getoond, niet gewijzigd) |
| Scanner | scanner_* |
| DSAR requests | dsar_* |
Tabellen
| Tabel | Inhoud |
|---|---|
{prefix}owc_ledger | Geketend toestemmingsregister |
{prefix}owc_dsar | Verzoeken van betrokkenen |
{prefix}owc_scanner | Vondsten van de scanner |
{prefix}owc_scripts | Signaturencatalogus van de blokkering |
{prefix}owc_form_links | Koppelingen 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
| Constante | Effect |
|---|---|
OWC_LEDGER_KEY | HMAC-sleutel van het register, en de afgeleide sleutel van de regiodetectiecookie |
OWC_TRUSTED_PROXY / OWC_TRUSTED_PROXIES | CIDR-reeksen van de vertrouwde proxy's; zonder die constanten worden de headers met het client-IP genegeerd |
OWC_GEO_TRUSTED_HEADERS | cloudflare, cloudfront, proxy of all |
OWC_BEHIND_CLOUDFLARE | Snelkoppeling voor de landheader van Cloudflare |
OWC_BEHIND_CLOUDFRONT | Snelkoppeling voor de landheader van CloudFront |
De developer hooks
Filters
| Filter | Rol |
|---|---|
owc_settings | Effectieve instellingen. Het antwoord moet afhangen van iets dat voor het hele request stabiel is |
owc_banner_texts | Teksten van de banner |
owc_bootstrap_config | Statische configuratie afgedrukt in <head> — nooit gegevens per bezoeker |
owc_scripts_catalog | Catalogusregels vóór de validatie |
owc_blocker_allowlist | Hosts die nooit worden geblokkeerd |
owc_scanner_urls | Te scannen URL's (nadien opnieuw tot de host van de site beperkt) |
owc_throttle_max | Budget van een rate limit; 0 schakelt ze uit |
owc_allowed_request_hosts | Aanvaarde hosts voor een publieke schrijfactie |
owc_allow_headerless_write | Heft het falen gesloten op voor schrijfacties zonder origin (standaard false) |
owc_geo_mmdb_path | Pad naar de MaxMind-database |
owc_tcf_purpose_map | Koppeling categorieën → TCF-doelen |
owc_tcf_stub_config | Statische configuratie van de TCF-stub |
owc_policy_template | Ruwe tekst van een sjabloon — schakelt het taalslot uit |
owc_policy_vars | Variabelen van een document |
owc_policy_html | Uiteindelijke HTML van een document |
owc_dsar_types | Aangeboden en aanvaarde soorten verzoeken |
owc_dsar_form_notice | Informatietekst onder het formulier |
owc_dsar_export_bundle | Inhoud van de portabiliteitsbundel |
owc_dsar_show_fulfilment_panel | Weergave van het uitvoeringspaneel |
Acties
| Actie | Signatuur |
|---|---|
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:
- Staat
banner_enabledaan? De badge bovenaan het beheerscherm zegt het. - Roept uw thema
wp_head()enwp_footer()aan? Zonder die twee weigert de banner zich af te drukken, in plaats van inerte markup te produceren. - Heeft de bezoeker al een keuze gemaakt? De banner verschijnt alleen bij
<html data-owc="none">. Test in een privévenster of metwindow.OWCBanner.reset(). - Heeft de bezoeker JavaScript? Zonder JavaScript blijft de banner verborgen — bewust — en
neemt het
<noscript>-blok het over. - 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.
- 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 deHost-header naast elkaar. De remedie is de site op één canonieke host serveren. consent_policy_hash_checkstaat aan en uw documenten zijn gewijzigd — dat is het gewenste gedrag, de bezoeker wordt na een wijziging van het beleid opnieuw bevraagd.- 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 filterowc_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/consentnooit een veto.
Fout ratelimit — 429
De limieten liggen bewust laag op publieke schrijfacties. Twee oorzaken:
- Een niet-verklaarde CDN of proxy: al uw bezoekers komen met hetzelfde adres binnen en
delen hetzelfde budget. Verklaar hem met
OWC_TRUSTED_PROXYinwp-config.php. - 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.
- Controleer of de tabel
{prefix}owc_ledgerbestaat. Het tabblad Audit ledger toont een leesbaar bericht “nog niet geïnstalleerd” in plaats van een wit scherm. - Controleer de schrijfrechten van de MySQL-gebruiker.
- Zet als laatste redmiddel
ledger_enabledtijdelijk uit om de dienst te herstellen — in het besef dat u ondertussen het bewijs verliest.
Er laden nog steeds trackers vóór de toestemming
- Staat
compliance_strictaan? Zonder die instelling worden alleen de door WordPress in de wachtrij gezette scripts behandeld. - 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.
- 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.
- 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. - Zet
blocker_unknown_script_policyopblock— 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
- Staat hij aan? Hij staat standaard uit.
- Werkt WP-Cron? Met
DISABLE_WP_CRONen zonder systeemcron gaat de geplande scan nooit af. Start er met de hand een om dat te controleren. - Is de HTTP-loopback mogelijk? Een HTTP-authenticatie op een staging-omgeving of een firewall blokkeert de scan. Het dekkingspaneel toont de eerste fout.
- 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.
- Het antwoord van de API bevat
mail_sent: falsewanneer het versturen is mislukt, en het formulier toont dan een specifiek bericht dat naar het contactadres verwijst. - Installeer een SMTP-plugin. De functie
mail()van PHP wordt door de meeste ontvangende servers geweigerd. - 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. - Controleer of
dsar_emaileen 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_idstaat 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 servervendor-list.consensu.orgkan bereiken via uitgaand HTTPS.
Alle bezoekers krijgen het geconfigureerde profiel, ondanks de regiodetectie
- Staat
geo_enabledaan? Het staat standaard uit. - Hebt u uw proxy verklaard? Zonder
OWC_BEHIND_CLOUDFLARE,OWC_BEHIND_CLOUDFRONTofOWC_GEO_TRUSTED_HEADERSinwp-config.phpworden de landheaders genegeerd, omdat ze te vervalsen zijn. - Staat het MaxMind-bestand op zijn plaats? De plugin downloadt het niet en biedt geen uploadscherm.
- 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?
- Supportportaal: https://optionweb.dev/nl/addons/support/
- E-mail: support@optionweb.dev
- Documentatie: https://optionweb.dev/nl/addons/ow-consent/