OW Consent — Documentazione
La gestione completa del consenso in WordPress: banner multi-giurisdizione, blocco dei traccianti, scanner, documenti legali, registro di prova e portale dei diritti. Autore: OptionWeb — Julien Daniel Pagina del plugin: https://optionweb.dev/it/addons/ow-consent/ Licenza: GPL-2.0-or-later Versione coperta da questo documento: 1.4.3
Indice
- Panoramica
- Installazione
- Avvio rapido
- Gli undici profili di conformità
- Le categorie di cookie
- Il banner
- Il blocco automatico
- Lo scanner dei traccianti
- Il generatore di documenti legali
- Il registro dei consensi
- Il portale dei diritti (DSAR)
- L'opt-out CCPA «Do Not Sell or Share»
- Google Consent Mode v2
- IAB TCF v2.2
- Global Privacy Control
- Il rilevamento regionale
- L'integrazione OW Forms
- Il pulsante flottante
- Shortcode
- REST API
- Riferimento delle impostazioni
- Risoluzione dei problemi
- FAQ
Panoramica
OW Consent è una suite di gestione del consenso per WordPress. Copre l'intera catena: mostrare una scelta, applicarla davvero ai traccianti, conservarne la prova, pubblicare i documenti che la spiegano e ricevere le richieste degli interessati.
Sono inclusi undici profili di conformità — dal GDPR al CCPA, passando per la Legge 25 del Québec, la LGPD brasiliana o la DPDP indiana. Il profilo attivo decide il modello di consenso (opt-in o opt-out), i valori predefiniti di Google Consent Mode, il testo dei documenti generati, i diritti pubblicati e l'autorità di controllo citata.
Tutto resta nel sito. Il plugin contatta un solo servizio esterno, la Global Vendor List di IAB Europe, e soltanto se il modulo TCF viene attivato — modulo disattivato di default. Nessuna telemetria, nessun account, nessun abbonamento.
Compatibile con una cache di pagina completa, per costruzione
È il punto di architettura che governa tutto il resto. Niente di ciò che il server produce dipende dal cookie di consenso. L'HTML è identico per tutti i visitatori: i traccianti sono riscritti in tag inerti per chiunque, ed è un runtime JavaScript a liberarli nel browser, categoria per categoria, leggendo il cookie prima del primo rendering.
Conseguenza pratica: LiteSpeed Cache, WP Rocket, Varnish o una CDN non possono servire le
scelte di un visitatore a un altro. Solo le risposte realmente personali — la pagina di
conferma dell'identità di una richiesta di diritti, le risposte REST che contengono dati
del visitatore — sono marcate esplicitamente come non memorizzabili in cache. Nemmeno un
nonce viene stampato in HTML memorizzabile: il banner ne recupera uno fresco su un endpoint
no-store subito prima di ogni scrittura.
Che cosa è incluso
- Banner di consenso: quattro posizioni, tema chiaro / scuro / automatico, «Rifiuta tutto» visibile quanto «Accetta tutto», pannello delle preferenze accessibile da tastiera, interamente traducibile.
- Blocco automatico dei traccianti: script, snippet inline, iframe, pixel, resource
hint (
preconnect,dns-prefetch,preload), fogli di stile analytics/marketing e media di terze parti, a partire da 175 firme incluse e modificabili. - Scanner dei traccianti: scansione delle pagine del sito tramite WP-Cron, lettura
delle intestazioni
Set-Cookie, sonda browser riservata agli amministratori, e un pannello che dice sempre che cosa la scansione ha realmente coperto. - Generatore di documenti legali: informativa privacy e cookie policy per gli undici profili, nella lingua della giurisdizione; note legali e condizioni generali in francese.
- Registro dei consensi: ogni azione aggiunta a una tabella concatenata tramite HMAC, con il profilo in vigore, l'impronta dei documenti pubblicati e l'impronta del banner realmente mostrato.
- Portale dei diritti (DSAR): modulo tramite shortcode, verifica via e-mail, termine dell'articolo 12.3 attivato alla conferma dell'identità, aggancio all'esportatore e al cancellatore nativi di WordPress.
- Google Consent Mode v2, IAB TCF v2.2, Global Privacy Control, rilevamento regionale, integrazione OW Forms: ciascuno attivabile in modo indipendente.
- REST API completa sotto il namespace
owc/v1.
Che cosa non fa — da leggere prima di impegnarsi
In materia giuridica, una promessa eccessiva espone chi la riceve quanto chi la formula. Ecco i limiti, così come sono scritti nel codice.
- I documenti generati sono modelli, non consulenza legale. Ogni documento si chiude con un avvertimento che lo dice, e l'avvertimento è attivo di default. È indispensabile far rileggere i documenti prima di pubblicarli.
- Il modulo TCF non è una CMP registrata presso IAB Europe. Richiede un CMP ID che va ottenuto per proprio conto, non offre alcuna scelta a livello di fornitore, e i fornitori hanno motivo di rifiutarne il segnale. Se un ricavo pubblicitario sotto TCF conta, conviene usare una CMP certificata.
- Il registro è a prova di manomissione solo a una condizione. La catena è una prova soltanto se la sua chiave di firma vive fuori dal database. Il plugin rileva il caso contrario, lo riporta da sé e lo segnala in amministrazione invece di sostenere il contrario.
- Lo scanner non esegue alcun JavaScript. Vede ciò che l'HTML contiene e ciò che le
intestazioni
Set-Cookiedepositano; quello che un tag manager inietta a runtime è visto solo da una sonda riservata agli amministratori connessi. - Il blocco predefinito non è «tutto ciò che è di terze parti». Uno script di terze parti sconosciuto è autorizzato di default; sono le iframe sconosciute a essere bloccate di default. Il blocco poggia sul catalogo di firme, che si può arricchire.
- Senza JavaScript nessuna scelta può essere registrata. Il visitatore vede un blocco
<noscript>e nulla di non essenziale viene caricato, ma nemmeno viene registrato nulla. - Nessuna granularità per singolo Stato negli Stati Uniti né per provincia in Canada
nel rilevamento regionale: tutto il territorio statunitense riceve il profilo
ccpa, tutto il Canada ricevequebec. - Note legali e condizioni generali esistono solo in francese. Per ogni giurisdizione la cui lingua di destinazione non è il francese, il generatore rifiuta di produrre questi due documenti invece di pubblicare un testo inadatto.
- Nessun ruolo dedicato: tutta l'amministrazione richiede la capability
manage_options.
Questo elenco è una scelta dichiarata, non una roadmap nascosta. Sulla conformità, uno strumento che enuncia i propri buchi vale più di uno strumento che li nasconde.
Installazione
Dallo .zip
- Scaricare
ow-consent-1.4.3.zipda https://optionweb.dev/it/addons/ow-consent/ - Plugin → Aggiungi nuovo → Carica plugin
- Scegliere il file, fare clic su Installa, poi su Attiva
Via FTP
Decomprimere l'archivio e copiare la cartella ow-consent in /wp-content/plugins/,
poi attivare il plugin da Plugin.
Requisiti
- WordPress 6.2 o più recente — è un rifiuto di avvio, non una raccomandazione (vedi sotto)
- PHP 7.4 o più recente
- MySQL 5.7+ / MariaDB 10.2+
- WP-Cron funzionante se si usano lo scanner, la conservazione del registro, i solleciti di scadenza DSAR o il modulo TCF
La salvaguardia WordPress 6.2. Dalla 1.3.0, ogni identificatore di tabella passa dal segnaposto
%idiwpdb::prepare(), che WordPress comprende solo a partire dalla 6.2. Su un core più vecchio,prepare()restituirebbe una stringa vuota, il catalogo dei traccianti si risolverebbe silenziosamente in un insieme vuoto, e il banner continuerebbe a promettere al visitatore un blocco che non avverrebbe. Il plugin rifiuta quindi di avviarsi e mostra un messaggio di errore che dice esplicitamente che nulla è bloccato e che nessun consenso viene registrato. L'intestazioneRequires at leastimpedisce già l'attivazione sotto la 6.2, ma non copre né un downgrade del core su un'installazione in servizio, né una copia depositata via FTP.
Che cosa viene installato
All'attivazione, OW Consent crea quattro tabelle:
| Tabella | Contenuto |
|---|---|
{prefix}owc_ledger | Il registro dei consensi, concatenato tramite hash |
{prefix}owc_dsar | Le richieste di esercizio dei diritti |
{prefix}owc_scanner | I rilevamenti dello scanner dei traccianti |
{prefix}owc_scripts | Il catalogo di firme usato dal blocco |
Una quinta tabella, {prefix}owc_form_links, viene creata a parte dall'integrazione
OW Forms se il plugin gemello è attivo (vedi L'integrazione OW Forms).
L'attivazione aggiunge anche:
- l'opzione
owc_settings, inizializzata vuota — deliberatamente: finché non si è salvato nulla, tutti i testi provengono dai valori predefiniti inglesi tradotti al volo nella lingua del sito, invece di congelare nel database la lingua di chi ha attivato il plugin; - l'opzione
owc_version; - il catalogo dei traccianti fornito (
data/tracker-catalog.json), inserito a blocchi di 100 inINSERT IGNORE, con un flag di inizializzazione per versione (owc_catalog_seeded_1.4.3); - l'attività quotidiana
owc_daily_maintenance, pianificata un'ora dopo l'attivazione.
Altri cinque eventi cron vengono armati dai moduli che li utilizzano. I sei hook cron del
plugin sono: owc_scanner_run, owc_scanner_run_batch, owc_ledger_retention,
owc_tcf_refresh_gvl, owc_run_upgrade e owc_daily_maintenance.
In multisito, le tabelle sono create per sito, mai condivise (wp_2_owc_ledger,
ecc.). Un'attivazione di rete percorre tutti i siti solo se la rete conta al massimo
200 siti; oltre, ogni sito viene predisposto in modo pigro alla sua prima richiesta. Un
sotto-sito creato dopo un'attivazione di rete viene predisposto dall'hook
wp_initialize_site.
Che cosa definire in wp-config.php
Nessuna di queste costanti è obbligatoria, ma due di esse cambiano il valore probatorio di ciò che il plugin produce.
// Consigliato: porta la chiave di firma del registro fuori dal database.
define( 'OWC_LEDGER_KEY', 'una lunga stringa casuale, propria di questo sito' );
// Consigliato: i salt standard di WordPress. Senza di essi, WordPress li memorizza nel
// database, e il registro lo segnala da sé come non a prova di manomissione.
define( 'AUTH_KEY', '…' );
define( 'AUTH_SALT', '…' );
// Se il sito sta dietro una CDN, un bilanciatore di carico o un reverse proxy.
// Senza questa costante, CF-Connecting-IP, X-Forwarded-For e X-Real-IP sono IGNORATI
// e viene usato solo REMOTE_ADDR — il che dà la stessa identità a tutti i visitatori
// ai fini della limitazione di frequenza.
define( 'OWC_TRUSTED_PROXY', '198.51.100.0/24, 2001:db8::/32' );
// `OWC_TRUSTED_PROXIES` è accettato come alias; vanno bene una stringa o un array.
// Solo per il rilevamento regionale: dichiara quali header di paese sono attendibili.
define( 'OWC_GEO_TRUSTED_HEADERS', 'cloudflare' ); // 'cloudflare'|'cloudfront'|'proxy'|'all'
define( 'OWC_BEHIND_CLOUDFLARE', true );
define( 'OWC_BEHIND_CLOUDFRONT', true );
OWC_LEDGER_KEY serve anche a derivare la chiave del cookie di rilevamento regionale. Se
non è definita, la chiave del registro ripiega su wp_salt('auth').
Chi ha accesso
Tutta l'amministrazione del plugin e tutte le rotte REST di amministrazione richiedono la
capability manage_options. Non esiste né un ruolo dedicato, né una capability più
fine: aprire la schermata del plugin a qualcuno equivale a consegnargli le impostazioni
del sito.
Percorso di aggiornamento
Il plugin non migra mai il proprio schema al volo su una pagina anonima. Quando la versione
cambia, owc_version e un flag owc_pending_upgrade vengono scritti immediatamente, poi:
- la migrazione viene eseguita inline se la richiesta è una richiesta di amministrazione fuori AJAX, un'esecuzione di cron o un comando WP-CLI;
- altrimenti viene pianificato un evento
owc_run_upgradecinque secondi dopo.
admin-ajax.php è trattato come una richiesta anonima: è un punto di ingresso pubblico. La
migrazione viene eseguita sotto lock (owc_upgrade_lock, sottratto dopo 300 secondi), e una
rete di sicurezza su admin_init recupera i siti in cui WP-Cron è disattivato. A parità di
versione, il costo totale è una sola get_option().
Disattivazione ed eliminazione
Disattivare conserva tutti i dati e cancella solo i sei eventi cron. In disattivazione di rete, tutti i siti vengono percorsi a lotti di 200 — a differenza dell'attivazione — perché un cron lasciato armato non sparirebbe mai.
Eliminare il plugin attiva uninstall.php, che procede in due tempi:
- Sempre, qualunque sia l'impostazione: i sei cron vengono cancellati e la tabella
{prefix}owc_dsarviene eliminata, insieme ai suoi transient di limitazione di frequenza. È l'unica tabella che contiene dati personali direttamente identificativi relativi a terzi (indirizzo e-mail, nome, testo libero); una volta rimosso il plugin, niente ne limita più la conservazione e nessuna schermata permette di rispondere, esportare o cancellare. Esportare le richieste da conservare prima di eliminare il plugin. - Solo se
delete_data_on_uninstallè esplicitamente attivo: eliminazione delle tabelleowc_ledger,owc_dsar,owc_scanner,owc_scriptseowc_form_links, delle opzioni nominate, di tutte le opzioni con prefissoowc_(transient compresi) e delleusermetacon prefissoowc_. In multisito, le opzioni di rete seguono la decisione del sito principale.
Questa impostazione è disattivata di default: la prova del consenso, richiesta dall'articolo 7.1 del GDPR, sopravvive all'eliminazione del plugin.
Avvio rapido
Dopo l'attivazione, aprire la voce OW Consent nella barra laterale dell'amministrazione. L'intero plugin sta in questa unica schermata, su dieci schede: Dashboard, Banner, Compliance, Legal identity, Categories, Policies, Scanner, Tracker catalogue, Audit ledger, DSAR requests.
Un salvataggio riguarda solo la scheda aperta. È deliberato: ogni booleano ha un campo nascosto gemello, e una chiave assente dal modulo significa «questo campo è su un'altra scheda», mai «deselezionato». Senza questo accorgimento, salvare una scheda sovrascriverebbe le impostazioni di tutte le altre.
1. Scegliere il profilo di conformità
Scheda Compliance. Il profilo decide il modello di consenso, i valori predefiniti di
Consent Mode, i documenti generati e i diritti pubblicati. Predefinito: gdpr.
compliance_strict è attivo di default: è ciò che estende il blocco oltre i soli
script registrati da WordPress. Conviene lasciarlo attivo se si vuole che le iframe, i
pixel e gli script scritti a mano nel tema vengano trattati.
2. Compilare l'identità legale
Scheda Legal identity. Questi campi sono la materia prima dei documenti generati, e la generazione è rifiutata finché un campo obbligatorio resta vuoto — con l'elenco delle chiavi mancanti, non un fallimento silenzioso.
Minimo per tutti i documenti: legal_company_name e legal_company_email. Per tutto
tranne la cookie policy, aggiungere l'indirizzo e il paese. Per le note legali, aggiungere
anche il telefono, il direttore della pubblicazione e i recapiti completi dell'hoster; in
Francia, Belgio e Lussemburgo diventano obbligatori anche la forma giuridica e il numero di
registrazione.
Lasciare legal_dpa_authority vuoto: l'autorità di controllo è derivata dal paese e dal
profilo. Compilarlo a mano su un sito multi-giurisdizione equivale a nominare il regolatore
sbagliato.
3. Verificare le categorie
Scheda Categories. Le sei categorie sono disponibili di default. Disattivare quelle
che il sito non usa: una categoria assente dall'interfaccia non è un guadagno di
conformità, è una risorsa bloccata per sempre senza un interruttore per liberarla — il
blocco la riversa allora su marketing.
Lasciare vuote le etichette finché vanno bene così: seguono allora la lingua del sito. Appena si personalizza un testo, quel testo smette di seguire la lingua.
4. Regolare il banner
Scheda Banner. Posizione, tema, pulsanti, etichette, durata di rinnovo.
Due punti da non mancare: lasciare attivo banner_reject_all («Rifiuta» deve essere
tanto semplice e visibile quanto «Accetta»), e lasciare disattivata la X di chiusura —
è assente di default perché una chiusura senza scelta equivale a un rifiuto implicito. Se
la si attiva, un clic sulla X esegue il percorso «Rifiuta tutto» completo, mai una chiusura
silenziosa.
5. Generare i documenti
Scheda Policies. Quattro documenti: cookie policy, informativa privacy, note legali, condizioni generali. Ciascuno diventa una pagina WordPress versionata, il cui link viene reiniettato nelle impostazioni.
Generare prima in anteprima, rileggere, poi pubblicare. E far rileggere da un professionista: la fascia di avvertimento in fondo a ogni documento non è decorativa.
6. Aprire il portale dei diritti
Creare una pagina e incollarci [owc_dsar_form]. Compilare dsar_email nella scheda
DSAR requests: è l'indirizzo di contatto pubblicato sotto il modulo e il destinatario
delle notifiche. In sua assenza, il plugin ripiega su legal_dpo_email, poi su
admin_email — ma admin_email non viene mai pubblicato su una pagina pubblica.
7. Verificare prima di aprire al pubblico
La dashboard esegue quindici controlli di conformità e distingue gli errori dagli avvisi: banner disattivato, «Rifiuta tutto» assente, X di chiusura attiva, identità legale incompleta, portale dei diritti chiuso, registro disattivato, nessuna informativa collegata, autorità di controllo in contraddizione con il profilo attivo (con un pulsante «Fix this» che la reimposta), sito non in HTTPS, traccianti non categorizzati, richieste DSAR fuori termine, regole di catalogo che non possono scattare.
Portare queste quindici righe al verde prima di dichiarare che il sito è conforme.
Gli undici profili di conformità
Il profilo di conformità non è cosmetico. Pilota il modello giuridico, l'interfaccia, i segnali tecnici e il contenuto dei documenti pubblicati.
L'elenco
| Profilo | Regime coperto | Modello |
|---|---|---|
gdpr | GDPR + ePrivacy (UE/SEE) | Opt-in |
uk_pecr | UK GDPR + PECR (Regno Unito) | Opt-in |
ch_nfadp | nLPD svizzera | Opt-in |
quebec | Legge 25 (Québec) | Opt-in |
lgpd | LGPD (Brasile) | Opt-in |
popia | POPIA (Sudafrica) | Opt-in |
pipl | PIPL (Cina) | Opt-in |
dpdp | DPDP Act 2023 (India) | Opt-in |
ccpa | CCPA / CPRA (California) | Opt-out |
us_generic | Leggi statali statunitensi generiche | Opt-out |
au | Privacy Act (Australia) | Opt-out |
Impostazione: compliance_profile, predefinito gdpr.
Opt-in significa che prima di qualsiasi scelta sono concesse solo le categorie forzate.
Opt-out significa che tutto è concesso fino al rifiuto. I tre profili opt-out sono
ccpa, us_generic e au. Il runtime JavaScript applica esattamente la stessa regola di
PHP, in modo che server e browser non possano riportare due stati diversi.
Che cosa cambia realmente il profilo
| Che cosa cambia | Dettaglio |
|---|---|
| Modello di consenso | Opt-in, salvo ccpa, us_generic, au |
| Valori predefiniti di Consent Mode | I sette segnali passano a granted sotto un profilo opt-out |
| Link «Do Not Sell or Share» obbligatorio | ccpa e us_generic soltanto |
| GPC giuridicamente vincolante | ccpa e us_generic soltanto — l'Australia è esplicitamente esclusa: regime opt-out, ma che non riconosce GPC |
| Perimetro «il GDPR si applica» per TCF | 27 paesi UE + IS, LI, NO + GB + CH, cioè 31 codici |
| Lingua del documento generato | quebec → francese; lgpd → portoghese; gdpr con paese FR, BE o LU → francese; tutto il resto → inglese |
| Autorità di controllo citata | Tabella per profilo, affinata per paese nel caso del GDPR |
| Elenco dei diritti pubblicato | Un elenco redatto per profilo, con citazione dell'articolo |
| Corpo «regime cookie» del documento inglese | Testo, perimetro di trasferimento e garanzie distinti per profilo |
I documenti generati per profilo
L'informativa privacy e la cookie policy esistono per tutti gli undici profili. I template
sono risolti in questo ordine, vince il primo trovato:
<tipo>_<profilo>_<lingua>, poi <tipo>_<profilo>, poi <tipo>_<lingua>, poi <tipo>.
I corpi inglesi della cookie policy sono consapevoli del profilo: regola applicabile,
perimetro di trasferimento, garanzie ed etichetta del pannello differiscono per gdpr,
uk_pecr, ch_nfadp, au, pipl, dpdp, ccpa e us_generic, con le citazioni
corrispondenti (art. 5(3) della direttiva 2002/58, PECR reg. 6, art. 45c(b) LTC e
art. 19/6(7)(b) nLPD, APP 8, art. 24 PIPL, sezioni 5/6/7/9(3)/16 del DPDP Act 2023,
§1798.121 e Cal. Code Regs. tit. 11 §7025, VCDPA/CPA/CTDPA/UCPA/TDPSA).
Autorità di controllo citata
| Profilo | Autorità nominata |
|---|---|
gdpr, paese FR | CNIL |
gdpr, paese BE | APD-GBA |
gdpr, paese LU | CNPD |
gdpr, paese DE | BfDI |
gdpr, altro paese SEE | Formulazione generica («l'autorità di controllo competente») |
uk_pecr | ICO |
ch_nfadp | PFPDT / 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 | Attorney General del proprio Stato |
Se legal_dpa_authority è stato modificato, il valore inserito prevale su questa
derivazione.
Limiti della copertura giurisdizionale
- Solo quattro paesi del SEE hanno un'autorità nominata (FR, BE, LU, DE). Un sito GDPR in Spagna, in Italia o nei Paesi Bassi pubblica una formulazione generica.
- Nessuna granularità per singolo Stato negli Stati Uniti. Il profilo
us_genericesiste e porta i propri testi VCDPA/CPA/CTDPA/UCPA/TDPSA, ma nessun rilevamento automatico lo assegna: si sceglie a mano. - Nessuna granularità provinciale in Canada.
- Tre sole lingue di documento: francese, inglese, portoghese. I corpi non passano dal meccanismo di traduzione di WordPress — è deliberato: un documento legale deve essere monolingue e la sua lingua segue la giurisdizione, mai la lingua dell'amministratore.
- Note legali e condizioni generali: template solo in francese. Il generatore rifiuta
seccamente per qualsiasi altra lingua di destinazione, a meno che non si fornisca il
proprio corpo tramite il filtro
owc_policy_template.
Le categorie di cookie
L'elenco canonico
Sei categorie, in questo ordine di visualizzazione, necessary sempre in testa:
| Slug | Etichetta fornita | Descrizione fornita |
|---|---|---|
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. |
I valori qui sopra sono i predefiniti inglesi memorizzabili. Su un sito in italiano il visitatore vede la traduzione — vedi il meccanismo di traduzione più sotto.
Disponibilità
Impostazione cat_<slug>_available, una per categoria. Tutte e sei sono disponibili di
default. La vecchia chiave cat_<slug>_enabled viene ancora letta come ripiego, senza
migrazione.
preferences e social sono fornite disponibili perché il catalogo iniziale classifica
YouTube, Spotify, SoundCloud, Instagram, X e Facebook in social: senza la categoria
nell'interfaccia, queste integrazioni resterebbero bloccate per sempre, senza alcun modo di
fare opt-in.
necessary è forzata: non può essere disattivata (art. 5.3 ePrivacy). Non ha quindi un
interruttore di disponibilità in amministrazione, solo un'etichetta e una descrizione.
Il meccanismo di traduzione
Impostazioni cat_<slug>_label e cat_<slug>_desc. Il valore memorizzato viene restituito
testualmente solo se è non vuoto e diverso dal predefinito inglese; altrimenti esce la
traduzione.
Conseguenza da conoscere: finché non si personalizza nulla, cambiare la lingua del sito cambia la lingua delle categorie. Appena si inserisce un testo proprio, quel testo resta congelato nella lingua in cui è stato scritto. La stessa regola vale per i testi del banner.
La descrizione tradotta di necessary è costruita a partire da due stringhe: la seconda
nomina i cookie del plugin stesso, owc_consent (fino a 13 mesi) e owc_geo (24 ore,
scritto solo quando il rilevamento regionale è attivo).
Corrispondenza Google Consent Mode v2 — vincolante
È la fonte di verità unica del plugin: il banner, il blocco e il bootstrap leggono tutti questa tabella, quindi non possono divergere.
| Categoria | Segnali 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 |
Sfumatura importante: se il sito espone la categoria functional,
functionality_storage viene tolto dall'elenco di necessary. Altrimenti sarebbe
concesso prima di qualsiasi consenso, pur appartenendo a una categoria facoltativa.
Corrispondenza TCF — solo descrittiva
functional → functional, analytics → measurement, marketing → advertising,
preferences → personalization, social → social_media.
Sono etichette di visualizzazione. La corrispondenza vincolante lato TCF è la tabella degli identificatori numerici delle finalità IAB, descritta nella sezione IAB TCF v2.2. Non collegare mai un comportamento TCF a queste cinque etichette.
Che cosa succede se una categoria sparisce
Se una regola del catalogo punta a una categoria che il sito non espone più — per esempio
social è stata disattivata — il blocco riversa la risorsa su marketing, o in mancanza
di questa sulla prima categoria facoltativa disponibile. Senza questa normalizzazione, la
risorsa resterebbe bloccata per sempre senza alcun interruttore per liberarla.
Il banner
Quando compare
Il banner viene reso su wp_footer con priorità 5, e le sue risorse sono messe in coda su
wp_enqueue_scripts. Esce immediatamente se una di queste condizioni è vera: contesto di
amministrazione, feed RSS, robots.txt, oppure banner_enabled disattivato. Esce anche se
wp_head non è mai stato attivato — un tema che non chiama wp_head() otterrebbe
altrimenti un markup inerte e senza stile.
L'HTML è identico per tutti i visitatori. Tutte le caselle del pannello sono rese su OFF
lato server e poi idratate lato client dal cookie. È il JavaScript ad applicare
<html data-owc="given|none"> in modo sincrono, prima del primo rendering; il CSS mostra il
banner solo su data-owc="none".
Corollario da conoscere: un visitatore senza JavaScript non vede mai il banner, quindi
non resta mai chiuso dietro una modale che non può chiudere. Vede invece un blocco
<noscript> che spiega che le sue preferenze non possono essere registrate e che nessun
cookie non essenziale viene caricato finché non è stata fatta una scelta.
Posizioni e tema
| Impostazione | Valori | Predefinito |
|---|---|---|
banner_position | bottom-bar, bottom-card, center-modal, top-bar | bottom-bar |
banner_style | auto, light, dark | auto |
Solo center-modal ottiene role="dialog", aria-modal="true", uno sfondo oscurato, una
trappola di focus e il tasto Esc. Le altre tre posizioni sono un role="region": Esc non
viene intercettato (il tema mantiene la propria gestione) e il focus non viene rubato al
primo rendering, cosa che spingerebbe un utente da tastiera oltre tutti i link di salto.
La palette scura si applica sotto prefers-color-scheme: dark con protezioni per non
sovrascrivere un tema che dichiara esplicitamente light. Il tema del sito può pilotare la
palette tramite variabili CSS: --owc-paper-tint, --owc-ink, --owc-smoke, --owc-fog,
--owc-ui, --owc-accent, --owc-accent-strong, --owc-accent-darker.
Dettagli di implementazione: z-index 99998 per il banner e 99997 per lo sfondo, supporto
di env(safe-area-inset-*) per i notch iOS, scostamento automatico sotto la barra di
amministrazione di WordPress in posizione alta.
I pulsanti
| Impostazione | Predefinito | Effetto |
|---|---|---|
banner_accept_all | true | Mostra «Accetta tutto» |
banner_reject_all | true | Mostra «Rifiuta tutto» |
banner_preferences | true | Mostra «Personalizza» |
banner_close_x | false | Mostra la X di chiusura |
banner_show_logo | true | Mostra il logo del sito |
«Accetta tutto» e «Rifiuta tutto» condividono la stessa classe di formattazione: stesso sfondo, stesso bordo, stesso peso, stesso riempimento. È la risposta al requisito CNIL / EDPB (linee guida 03/2022): rifiutare deve essere semplice e visibile quanto accettare.
La X di chiusura
È disattivata di default, perché una X di chiusura equivale a un rifiuto implicito. Tre comportamenti da conoscere:
- Quando
banner_close_xè off, la X è presente nel markup ma porta l'attributohidden, e il CSS la rimuove del tutto: né visibile, né raggiungibile col focus, né annunciata agli screen reader. - Quando è attiva, fare clic sulla X esegue il percorso «Rifiuta tutto» completo, mai una chiusura silenziosa.
- Viene rivelata dal runtime, e rietichettata, in un solo caso: quando un rifiuto non ha potuto essere registrato affatto. In quel caso non rifiuta più nulla, mette via l'avviso — e quella chiusura non viene conteggiata come una scelta. Un visitatore la cui decisione viene rifiutata dal server non resta davanti a un banner che non può chiudere.
Il primo livello
- Il titolo (
text_title) e il messaggio (text_message). Il messaggio è l'unico campo con HTML ricco del plugin; viene reso con unwp_kseslimitato a<a href target rel>. - Il link all'informativa privacy:
link_privacy_policy, con ripiego sulla pagina di informativa dichiarata in WordPress se l'impostazione è vuota. - Il link alla cookie policy:
link_cookie_policy, senza ripiego. - Il nome del titolare del trattamento:
legal_company_name, altrimenti il nome del sito, mostrato come «Titolare del trattamento: …» (art. 13(1)(a)). - Il logo: il logo personalizzato del tema in dimensione
medium, altrimenti l'icona del sito.
Il pannello delle preferenze
Una riga per categoria disponibile, dentro un role="group" nominato.
- Una categoria forzata mostra un badge testuale «Always active», senza interruttore.
- Una categoria facoltativa mostra un
<button role="switch">conaria-checked,aria-labelledbyearia-describedby. Lo stato è portato daaria-checked, dalla posizione del cursore e da una parola visibile On/Off — mai dal solo colore. - Area di tocco ingrandita a 44 px su schermo piccolo (WCAG 2.2 AA, criterio 2.5.8).
- La barra delle azioni del pannello è
stickyin basso, perché «Salva le mie scelte» resti raggiungibile mentre l'elenco scorre.
Se il modulo TCF è attivo, compaiono due blocchi aggiuntivi: le finalità TCF raggiungibili e le funzionalità speciali dichiarate. Vedi IAB TCF v2.2.
Rinnovo del consenso
| Impostazione | Predefinito | Limiti |
|---|---|---|
consent_renewal_months | 12 | da 0 a 13 |
consent_policy_hash_check | true | — |
0 non significa «non richiedere mai più». Il valore 0 viene convertito in 13 mesi, e
13 mesi è il tetto rigido (delibera CNIL 2020-091). La durata effettiva è quindi sempre
compresa tra 1 e 13 mesi, ed è questo stesso valore a governare la durata del cookie, il
controllo di scadenza lato server e la durata pubblicata nei documenti generati. La costante
OWC_COOKIE_TTL visibile nel codice sorgente è solo un ripiego.
consent_policy_hash_check richiede di nuovo il consenso quando i documenti cambiano: il
cookie porta un'impronta delle pagine di informativa collegate, e una differenza riapre il
banner. L'impronta vale la stringa vuota quando nessuna pagina è collegata — è ciò che
permette di distinguere «sconosciuto» da «cambiato» e di non risollecitare mai un intero
sito su un confronto vuoto.
I testi
Sette slot, tutti vuoti di default e quindi tradotti automaticamente: text_title,
text_message, text_accept_all, text_reject_all, text_preferences, text_save, e
floating_button_label per il pulsante flottante.
In amministrazione, ogni campo mostra il proprio predefinito tradotto come segnaposto: lasciare vuoto conserva il predefinito che segue la lingua del visitatore.
Il filtro owc_banner_texts permette di sostituire questi testi via codice. Sono accettati
slot aggiuntivi; i valori non scalari o vuoti vengono scartati perché una callback maldestra
non possa svuotare il banner.
Come viene realmente registrata la scelta
Il runtime JavaScript è in ES5 stretto — niente arrow function, niente template literal — per funzionare nei browser integrati nelle app e nelle vecchie WebView. Vale la pena conoscere la sua sequenza di scrittura, perché è quella che spiega la maggior parte dei messaggi di errore.
- Riancoraggio dell'origine. Le URL REST provengono dalla configurazione di WordPress.
Se i visitatori navigano su un altro host (www contro apex, alias, dominio di anteprima,
proxy che riscrive
Host), quell'URL è cross-origin e il browser rifiuta di memorizzare ilSet-Cookiementre WordPress risponde 200. Il runtime riancora quindi il percorso fornito da PHP sull'origine realmente visitata. - Nonce. Recuperato fresco su
GET /owc/v1/noncesubito prima della scrittura, mai incorporato in HTML memorizzabile in cache, inviato nell'headerX-OWC-Nonce. Il recupero è limitato a 4 secondi, e un nonce assente, vuoto o irraggiungibile non impedisce mai la scrittura né viene segnalato al visitatore. page_urlesplicito nel corpo della richiesta, perché la riga di registro non dipenda dall'headerReferer, che un'estensione, un meta-referrer o un proxy possono rimuovere.- Protezione anti-blocco di 15 secondi con
AbortController: la richiesta viene annullata, non solo ignorata, perché un POST tardivo non scriva una seconda riga. - Definizione di successo. Il POST ha successo se, e solo se: la risposta è
ok, il corpo è JSON analizzabile,json.ok === true, e il cookie viene riletto nel browser. Una pagina di cache, una challenge di edge o un WAF serviti in 200 non contano. - Se il server risponde
cookie_set: false, il runtime scrive lui stesso il cookie con i parametri restituiti, poi rilegge. Un fallimento resta un fallimento: nulla viene pubblicato, nulla viene sbloccato. - Un solo reinvio, e solo su un 403 il cui codice è
owc_bad_nonce,rest_cookie_invalid_nonceorest_nonce_invalid, e solo se il nonce ottenuto è realmente diverso. - Ciò che il runtime pubblica è ciò che il server ha memorizzato: le categorie rifiutate dal server vengono sottratte dallo stato locale.
Finché il POST non è riuscito, nulla viene smascherato, nulla viene sbloccato, e nessun segnale Consent Mode «granted» viene emesso.
La tassonomia degli errori
Il runtime distingue nove cause, ciascuna con il proprio messaggio visibile: network,
refused, ratelimit, unexpected, timeout, browser, config, cookie,
owc_cookie_not_persisted. Lo stato HTTP e il codice del server sono applicati
sull'elemento (data-owc-status, data-owc-code) e registrati una volta in
console.warn — mai resi come testo visibile. Uno screenshot di supporto nomina quindi la
causa senza bisogno di una traccia di rete.
L'API JavaScript del banner
window.OWCBanner.show(); // apre in modalità banner
window.OWCBanner.hide(); // chiude
window.OWCBanner.openPreferences(); // apre il pannello delle preferenze
window.OWCBanner.openDnsmpi(); // apre il pannello con marketing, social
// e preferences già su OFF (ingresso CCPA)
window.OWCBanner.reset(); // cancella il cookie lato client, invia una revoca,
// poi ricarica la pagina
window.OWCBanner.acceptCategory( 'social' ); // restituisce una promise risolta a true
// solo se il server ha davvero memorizzato
Un bootstrap più leggero viene stampato in <head> con priorità 1 ed espone
window.OWConsent: .config, .categories, .geo, .profile, .optOut, .state,
.has(cat), .refresh(), .paint(), .gcmSignals(state). La sua configurazione è
filtrabile tramite owc_bootstrap_config — ma tutto ciò che vi si trova è pubblico e
condiviso dalla cache: non inserirvi mai un dato proprio di un visitatore.
Aprire il pannello dalle proprie pagine
Tre modi, tutti equivalenti:
- il frammento di URL
#owc-preferences(o#owc-dnsmpi), considerato al caricamento e suhashchange; - qualsiasi elemento con la classe
owc-open-preferenceso l'attributodata-owc-open; - l'attributo
data-owc-dnsmpi="1"per forzare il comportamento «Do Not Sell».
Un evento owc:consent-changed viene diffuso a ogni cambiamento, con il payload inviato
nel dettaglio. È ascoltato dal bootstrap, dal pulsante flottante, dal runtime CCPA e dal
modulo TCF; lo si può ascoltare anche dall'esterno.
Limiti del banner
- Tutto dipende da
wp_head()ewp_footer(): un tema che non li chiama non ottiene nulla. - Senza
window.fetchewindow.Promise, nessuna scrittura è possibile e viene mostrato l'errorebrowser. - Un consenso può essere registrato lato server senza essere conservato lato browser (indirizzo del sito diverso dall'host visitato, storage bloccato, jar pieno). Il runtime rileva questo caso, rifiuta di contarlo come un successo, mostra un messaggio dedicato e trasmette il fatto al server alla scrittura successiva.
- Il banner non ha shortcode: compare ovunque o da nessuna parte.
Il blocco automatico
È il modulo che trasforma una scelta in un effetto reale. Riscrive i tag portatori di traccianti prima di qualsiasi consenso, per tutti, e lascia che sia il runtime a liberarli nel browser.
Due modalità, una sola impostazione
compliance_strict, attiva di default.
- Disattivata (modalità permissiva): è collegato solo il filtro
script_loader_tag. In altri termini, vengono riscritti solo gli script registrati dawp_enqueue_script(). Uno<script>scritto a mano nel tema, un embed, un'iframe, un pixel<img>: nulla viene toccato. - Attivata (modalità strict): oltre al filtro, un buffer di output cattura l'intero
documento, e viene trattato anche il markup reso tramite la REST API. In amministrazione,
se la richiesta è un
admin-ajax.php, un buffer separato copre le risposte che servono anche il front («carica altro», archivi filtrati) — ma solo se il gestore ha dichiarato esso stessoContent-Type: text/html.
Che cosa non viene mai bufferizzato
Il buffer strict esce immediatamente per: l'amministrazione, le richieste AJAX, i feed,
robots.txt, i trackback, il cron, le favicon, le richieste REST, le richieste JSON,
l'anteprima del personalizzatore, le sitemap, e i punti di ingresso wp-login.php,
wp-register.php, wp-signup.php, xmlrpc.php, wp-cron.php, wp-trackback.php —
verificati sul nome esatto dello script, mai come sottostringa dell'URL. Esce anche per le
richieste dello scanner, che hanno bisogno dell'HTML grezzo.
I cinque passaggi
La riscrittura avviene tramite espressioni regolari calibrate, mai tramite un parser DOM. Tre salvaguardie inquadrano il tutto: un documento di oltre 8 MB viene restituito intatto, un errore del motore PCRE restituisce il documento intatto, e se il risultato pesa meno della metà della dimensione originale, viene restituito l'originale. In altri termini, la modalità di guasto del blocco è «pagina servita senza blocco», mai «pagina rotta».
Passaggio 0 — mascheramento. Commenti HTML, <style> e <textarea> vengono sostituiti
da marcatori a base di caratteri di controllo, perché nessun pattern successivo trovi
corrispondenze al loro interno.
Passaggio 1 — <script>. Decisione, nell'ordine:
- già trattato → intatto;
- script del plugin stesso → intatto;
type="text/plain"o type non-JS (ld+json,importmap,x-template) → intatto;- URL cercata in
src,data-src,data-rocket-src,data-lazy-src,data-litespeed-src,data-cfsrc— gli attributi dei plugin di performance sono quindi coperti; - host in allowlist → intatto; risorsa first-party che non sembra un tracciante → intatto;
- corrispondenza al catalogo sulla forma
host + percorso; una URLdata:ojavascript:viene decodificata e valutata come un corpo inline; - una regola classificata
necessarynon viene mai bloccata (Stripe.js, reCAPTCHA, Turnstile, cdnjs…): bloccarla non produce alcuna conformità e rompe lo snippet chiamante; - nessuna corrispondenza →
blocker_unknown_script_policy, predefinitoallow; - altrimenti riscrittura in
type="text/plain"condata-owc-cat,data-owc-vendor,data-owc-srce, se il type originale era particolare (module, per esempio),data-owc-typeper non degradare lo script ripristinato.
I corpi inline vengono confrontati prima con le regole di catalogo di tipo
inline_signature, poi con quattordici firme ancorate scritte nel codice: fbq(,
_fbq.push, gtag(, dataLayer.push(, ga('…, _gaq.push, _paq.push, hjid,
clarity(, ttq.load|track|page, snaptr(, twq(, lintrk(, pintrk(. Eccezione
esplicita: un gtag('consent', …) da solo, cioè una dichiarazione Consent Mode
predefinita senza altri marcatori, resta eseguito.
Passaggio 2 — <iframe>. Stessi attributi di URL, più data-original e data-srcset.
First-party o allowlist → intatto. necessary → intatto, altrimenti un modulo con captcha
diventerebbe impossibile da inviare. Senza corrispondenza →
blocker_unknown_iframe_policy, predefinito block. L'iframe bloccata diventa
src="about:blank" e viene avvolta in un sostituto visivo contenente un pulsante
«Accetta
Passaggio 3 — <img>, solo pixel di tracciamento. Regola importante: una riga di
catalogo da sola non basta mai a neutralizzare un'immagine. Se l'immagine corrisponde al
catalogo ma la sua categoria non è né analytics né marketing — una CDN di immagini, un
servizio di gravatar, un hoster di font — viene lasciata intatta, altrimenti si
cancellerebbero i media del sito invece di un tracciante. Solo l'euristica di tag può
bloccare: undici punti di raccolta noti (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) o un'immagine di terze parti di 1×1
pixel. Il src viene allora sostituito da una GIF trasparente, e tutti gli attributi
portatori di URL vengono rimossi perché un lazy loader non ripristini l'originale.
Passaggio 4 — <link>. Condizionato a blocker_block_resource_hints, predefinito
true. Tratta solo gli host già presenti nel catalogo, mai uno sconosciuto.
- I resource hint (
preconnect,dns-prefetch,prefetch,prerender,preload,modulepreload) verso un terzo catalogato vengono rimossi, non rinviati: un hint apre una connessione TCP+TLS e divulga l'IP e l'impronta TLS del visitatore, e non c'è nulla da ripristinare in seguito. - Un foglio di stile classificato
analyticsomarketingviene neutralizzato. - Google Fonts e gli altri host di tipografia classificati
functionalnon vengono mai toccati: neutralizzarli dà un testo con font di ripiego su tutto il sito, per nessun guadagno di conformità.
Passaggio 5 — <object>, <embed>, <source>, <video>, <audio>. Mai blocco
predefinito: viene trattato solo un terzo già catalogato. Gli attributi autoplay e
preload vengono rimossi.
Che cosa conta come «first party»
L'URL del sito, l'URL di WordPress, l'URL dei contenuti, l'URL degli includes, i loro
equivalenti di rete in multisito, e la base dei caricamenti. Il www. viene tolto al
confronto. Qualsiasi percorso che inizia con /wp-content/ o /wp-includes/ è
first-party qualunque sia l'host, per coprire le riscritture CDN. Gli schemi non-HTTP
(data:, blob:, javascript:) non sono mai first-party.
Contro-eccezione: una URL che contiene gtag, gtm.js, analytics, pixel, fbevents,
hotjar, matomo, piwik o clarity viene trattata anche sull'host del sito. È ciò
che intercetta un GTM o un Matomo self-hosted, e i proxy first-party.
Il runtime di sblocco
Stampato in <head> con priorità 2. La sua configurazione JSON contiene solo il nome del
cookie e l'elenco degli host sorvegliati — nulla che dipenda dal visitatore.
- Lettura di stato: il cookie
owc_consentviene decodificato fino a stabilità, con un massimo di tre passaggi, perché i cookie scritti prima della 1.4.3 erano codificati due volte. - Protezione degli script iniettati dinamicamente: il setter
HTMLScriptElement.prototype.srcesetAttributevengono avvolti. Un loader first-party che assegnas.src = 'https://www.googletagmanager.com/gtm.js?id=…'viene intercettato e l'elemento marcato prima dell'esecuzione. L'elenco sorvegliato conta venti host: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. Tutto ciò che non vi figura passa indisturbato. Gli host della propria allowlist vengono tolti da questo elenco prima della stampa. - Reiniezione nell'ordine del documento. Uno script esterno ripristinato blocca la coda
fino al suo
onloadoonerror, con un ritardo massimo di 5 secondi perché un fornitore irraggiungibile non blocchi il resto. L'attributoasyncviene applicato solo se era presente in origine — senza questo, uno script creato concreateElementsarebbe forzato in asincrono e lo snippet di configurazione verrebbe eseguito prima della sua libreria. Per uno script inline ripristinato,document.writeviene temporaneamente reindirizzato per non cancellare il documento. - Revoca del consenso: se una categoria già applicata passa esplicitamente a
false, il runtime ricarica la pagina. Uno script già eseguito non può essere scaricato (art. 7.3 GDPR). Una categoria semplicemente assente dal payload non è una revoca. - Un
MutationObserverriesamina il markup iniettato dopo il caricamento (AJAX, sezioni lazy). - Un clic sul pulsante di un embed bloccato chiama
OWCBanner.acceptCategory(); se l'API del banner non esiste, l'embed viene sbloccato localmente, senza persistere nulla.
Il catalogo di firme
Tabella {prefix}owc_scripts. Il file fornito, data/tracker-catalog.json, contiene
175 righe che coprono 71 fornitori distinti:
| Ripartizione | Dettaglio |
|---|---|
| Per bersaglio | 110 pattern di URL, 65 nomi di cookie |
| Per tipo | 172 frammenti di testo, 3 espressioni regolari |
| Per categoria | necessary 55, marketing 37, functional 37, analytics 36, social 10 |
Colonne: pattern, pattern_type (host, regex, inline_signature), match_target
(url o cookie), name, vendor, category, privacy_policy_url, gcm_signal,
tcf_vendor_id, retention_days.
Due comportamenti da conoscere:
- Le righe
match_target = 'cookie'non sono mai usate dal blocco. Servono unicamente alla tabella dei cookie dei documenti generati. Confrontarle come sottostringa di URL è esattamente ciò che rompeva il JavaScript dei siti. - Un pattern di tipo
hostche punta a una URL, di meno di sei caratteri e senza punto, viene rifiutato: «fr» o «IDE» corrisponderebbero a «frame.js» e «provider.js».
Il catalogo viene messo in cache nell'object cache (1 ora) e in un transient (12 ore). Un
risultato vuoto non viene mai messo in cache. Ogni scrittura attiva l'azione
owc_catalog_updated, che svuota queste cache.
La scheda Tracker catalogue permette di aggiungere, modificare, eliminare e cercare regole, con un filtro «solo le regole che non possono scattare»: pattern vuoto o invisibile, espressione regolare che non compila o che fa backtracking catastrofico, pattern troppo corto e senza punto per una URL, categoria che non esiste più. Questa diagnosi è fatta in PHP, perché solo il motore PCRE può dire se un'espressione compila.
Impostazioni del blocco
| Impostazione | Predefinito | Valori |
|---|---|---|
compliance_strict | true | booleano |
blocker_unknown_script_policy | allow | allow, block |
blocker_unknown_iframe_policy | block | allow, block |
blocker_block_resource_hints | true | booleano |
blocker_allowlist | '' | un host per riga |
Punti di estensione
// Host mai bloccati, in aggiunta a blocker_allowlist.
add_filter( 'owc_blocker_allowlist', function ( array $hosts ) {
$hosts[] = 'cdn.mio-partner.example';
return $hosts;
} );
// Righe del catalogo prima della validazione.
add_filter( 'owc_scripts_catalog', function ( array $rows ) {
$rows[] = array(
'pattern' => 'tracker.example.com',
'pattern_type' => 'host',
'match_target' => 'url',
'name' => 'Esempio',
'vendor' => 'Esempio SpA',
'category' => 'analytics',
);
return $rows;
} );
Il metodo pubblico OWC_Blocker::block_html_fragment( $html ) applica il blocco a un
frammento che non è passato dal buffer di output.
Limiti del blocco
- Senza
compliance_strict, vengono trattati solo gli script messi in coda da WordPress. - Uno script di terze parti sconosciuto è autorizzato di default. Il blocco reale poggia sul catalogo e sulle firme inline — vanno arricchiti.
- La protezione degli script iniettati da JavaScript copre solo venti host; un tracciante fuori elenco iniettato da codice first-party passa.
- I fogli di stile classificati
functional(font web) sono volutamente lasciati passare. - Un tracciante catalogato in
<img>ma classificato diversamente daanalyticsomarketingnon viene neutralizzato. - I media (
object,embed,source,video,audio) non sono mai bloccati di default: viene trattato solo un terzo già catalogato. - Un documento di oltre 8 MB, un errore PCRE o una perdita di oltre il 50% del contenuto danno una pagina servita senza alcun blocco, silenziosamente.
- La revoca del consenso provoca un ricaricamento completo della pagina.
Lo scanner dei traccianti
Lo scanner inventaria ciò che le pagine caricano realmente. È disattivato di default: si attiva nella scheda Scanner.
Tre fonti di rilevamento
Ogni rilevamento conserva la propria provenienza e l'informazione «il blocco può agire su questo elemento».
1. L'HTML servito. Lo scanner recupera un campione delle URL del sito stesso e legge
il markup: <script src> e <script data-owc-src> — gli script già bloccati sono quindi
visti comunque —, firme inline, <iframe>, fogli di stile esterni e immagini la cui URL
contiene 1x1, pixel, track, beacon o impression. Nessun JavaScript viene
eseguito: ciò che un tag manager inietta a runtime è invisibile per questo passaggio.
2. Le intestazioni Set-Cookie. Sono gli unici cookie che la scansione lato server può
dimostrare, compresi i cookie HttpOnly che una sonda browser non vedrà mai. Sono
marcati non bloccabili: nulla nel blocco lato client può trattenere un cookie emesso dal
server. I cookie del plugin stesso vengono ignorati.
3. La sonda browser. Stampata nel footer solo per un utente connesso che dispone di
manage_options, e solo se lo scanner è attivo. Fotografa document.cookie al
caricamento, osserva le mutazioni del DOM — è ciò che intercetta quello che un tag manager
inietta —, interroga document.cookie ogni 5 secondi per i cookie scritti lato client, e
trasmette le sue osservazioni ogni 2 secondi e su beforeunload. La risposta che porta la
sonda è marcata come non memorizzabile in cache.
Limite riconosciuto nel codice: un amministratore ha in genere già accettato tutto. La sonda descrive quindi lo stato post-consenso, non lo stato pre-consenso.
La scansione
La coda delle URL è costruita per coprire template, non pagine: prima le URL principali (home, pagina iniziale statica, pagina degli articoli, e le quattro pagine di informative collegate), poi un alternarsi round-robin per famiglia — fino a 30 pagine, 15 articoli, 5 per tipo di contenuto pubblico personalizzato, 3 categorie e 3 tag. Senza questo alternarsi, trenta pagine quasi identiche divoravano tutto il budget e i template WooCommerce non venivano mai raggiunti.
Il filtro owc_scanner_urls permette di aggiungere URL, ma il risultato viene
nuovamente ristretto all'host del sito: lo scanner non esce mai dal dominio.
Ogni richiesta viene effettuata seguendo i redirect a mano (due salti al massimo, ogni salto
riverificato contro l'host del sito), con una risposta limitata a 2 MB, un header
Cache-Control: no-cache, no-store e un parametro di URL univoco per non leggere una pagina
di cache, e uno user-agent OW-Consent-Scanner/<versione>. Una risposta non-HTML è contata
come ignorata, non come un fallimento.
Un segreto di bypass viene inviato nell'header X-OWC-Scanner perché il blocco si
ritiri e lasci vedere l'HTML grezzo. Viene confrontato a tempo costante, e l'header è
verificato per primo perché un visitatore ordinario non attivi nemmeno la lettura
dell'opzione.
Budget, lock, ripresa
| Vincolo | Valore |
|---|---|
| URL per esecuzione | scanner_max_urls, predefinito 25, limiti da 1 a 500 |
| Timeout per richiesta | scanner_timeout, predefinito 8 s, limiti da 1 a 60 |
| Budget di tempo reale per lotto | max_execution_time − 10 s, altrimenti 45 s, limitato tra 5 e 60 s |
| Lock di esecuzione | 15 minuti |
| Durata di vita della coda | 6 ore |
Se il budget si esaurisce, una ripresa viene pianificata un minuto dopo e la scansione continua dal punto in cui si era fermata. Un secondo avvio mentre una scansione è in corso risponde «una scansione è già in corso». Corto circuito: se tre richieste falliscono senza che nessuna pagina sia stata letta, la scansione si ferma invece di bruciare il proprio tempo su altre ventidue URL — è il caso tipico di un sito il cui loopback HTTP è bloccato.
Le firme inline riconosciute
Ventitré aghi, raggruppati in ventun etichette: 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.
La chiave di deduplicazione è la firma, mai un'impronta del corpo: un vero snippet gtag o Pixel contiene valori propri della pagina, il che produrrebbe una riga per pagina.
Il ciclo di retroazione: rilevamento → catalogo → blocco
È il punto che rende utile lo scanner. Classificare un tracciante scrive una regola nel catalogo di blocco. Un clic sulla categoria di un rilevamento fa tre cose: scrive la categoria sulla riga, memorizza la decisione manuale, e crea la regola corrispondente. Alla fine di ogni scansione completa, la stessa operazione viene eseguita in blocco per ciò che il catalogo non sa già classificare.
Regole di conversione:
- un rilevamento di tipo
cookiediventa una regolamatch_target = cookiesul nome del cookie; - un rilevamento di tipo
inline_scriptscrive una regola solo se l'ago originale viene ritrovato. Senza di esso non viene scritta alcuna regola: una regola costruita a partire dall'etichetta «Google gtag (config)» sarebbe morta per sempre e corrisponderebbe al nome del rilevamento, il che lo farebbe passare per «coperto» per sempre; - altrimenti, una regola
hostsul dominio, rifiutata se il dominio è vuoto.
Le decisioni manuali sono memorizzate a parte (500 al massimo) e riapplicate a ogni nuova osservazione, perché il flag «confermato» significa anche «una regola di catalogo ha trovato corrispondenza» e non può quindi portare da solo l'informazione «l'amministratore ha deciso». Riclassificare come «non categorizzato» elimina la voce: è un vero ritorno indietro.
Il catalogo viene confrontato partendo dal pattern più lungo, perché una regola ampia
(google-analytics.com) non sovrascriva la regola più precisa scritta a mano
(www.google-analytics.com).
Pianificazione, avvisi, conservazione
scanner_frequency accetta hourly, twicedaily, daily e weekly — ma
l'amministrazione propone solo le ricorrenze che l'installazione conosce davvero.
Predefinito: weekly. Disattivare lo scanner rimuove dalla pianificazione i due eventi.
Due e-mail distinte, mai entrambe insieme:
- Traccianti non categorizzati — inviata solo per identificatori realmente nuovi, con una memoria limitata a 500 voci.
- «La scansione non è riuscita a leggere questo sito» — limitata a un messaggio a settimana e per firma di errore. È la modalità di guasto più grave, perché una scansione che non legge nulla non produce alcun rilevamento, quindi nessun avviso del primo tipo.
Destinatario: scanner_alert_email, altrimenti l'indirizzo di amministrazione del sito.
Conservazione: 90 giorni. I rilevamenti non rivisti da 90 giorni vengono eliminati, non appena almeno una pagina è stata letta — non solo quando una scansione si conclude.
Il pannello di copertura
È la parte più importante della scheda Scanner, e alimenta anche il riquadro di avvertimento dei documenti generati. Risponde sempre a queste domande:
- una scansione è già stata avviata?
- l'ultima si è conclusa, o è stata interrotta dal proprio budget di tempo?
- quante pagine sono state realmente recuperate (non tolte dalla coda)?
- quante non sono state raggiunte, e qual è stato il primo errore?
- un browser ha già riportato qualcosa, o non è mai stato osservato alcun JavaScript?
- qual è la finestra oltre la quale un tracciante non rivisto sparisce?
Una scansione che non ha raggiunto alcuna pagina è presentata come un fallimento, non come un risultato pulito. È questo pannello da leggere prima di pubblicare una cookie policy costruita su questi rilevamenti.
La scheda Scanner
Tre schede riepilogative (traccianti rilevati, non categorizzati, ultima scansione e
prossima esecuzione), il pannello di copertura, un pulsante «avvia una scansione ora», il
modulo di pianificazione, poi l'elenco dei rilevamenti: filtri per categoria, ricerca
testuale, filtro per tipo di elemento (script, inline_script, iframe, stylesheet,
pixel, cookie, link, preconnect), filtro per stato, export CSV, paginazione a 25.
Classificazione singola o in blocco — il selettore di blocco ha un primo elemento vuoto
disattivato, perché un invio accidentale non diventi una declassificazione generale.
Impostazioni dello scanner
| Impostazione | Predefinito | Limiti |
|---|---|---|
scanner_enabled | false | booleano |
scanner_frequency | weekly | hourly, twicedaily, daily, weekly |
scanner_max_urls | 25 | da 1 a 500 |
scanner_timeout | 8 | da 1 a 60 |
scanner_probe_mode | admins | admins, off |
scanner_alert_email | '' | indirizzo e-mail |
Da sapere:
scanner_probe_modenon è consumato in questa versione. Il codice lo segnala esplicitamente — la condizione di stampa della sonda non legge più questa impostazione e si basa unicamente suscanner_enabled. Salvareoffmemorizza il valore senza disattivare la sonda. Per fermare davvero la sonda, disattivare lo scanner.
Limiti dello scanner
- Nessun JavaScript viene eseguito dalla scansione lato server. Senza la sonda, ciò che un tag manager inietta è assente dall'inventario.
- La sonda è riservata agli amministratori connessi e non esiste alcuna modalità che la esegua per un visitatore ordinario: richiederebbe una propria informativa preventiva.
- Un cookie posato tramite header
Set-Cookieviene rilevato ma non è bloccabile. - La scansione non esce mai dal dominio ed è limitata a 25 URL di default: un sito grande non è mai coperto integralmente.
- Su un host in cui il loopback HTTP è bloccato (autenticazione HTTP di staging, firewall), la scansione non legge nulla.
- Un rilevamento non rivisto da 90 giorni sparisce, e sparisce quindi anche dalla cookie policy.
- Lo scanner dipende da WP-Cron: su un sito con
DISABLE_WP_CRONe senza cron di sistema, la scansione pianificata non parte.
Il generatore di documenti legali
I quattro documenti
| Tipo | Contenuto | Lingue disponibili |
|---|---|---|
cookie_policy | Cookie policy | francese, inglese, portoghese |
privacy_policy | Informativa privacy | francese, inglese, portoghese |
legal_notice | Note legali | solo francese |
terms | Condizioni generali | solo francese |
Ogni documento generato è una pagina WordPress, versionata tramite metadati:
_owc_policy_type, _owc_policy_version (incrementata a ogni generazione),
_owc_policy_hash (impronta SHA-256 dell'HTML), _owc_policy_generated_at,
_owc_policy_profile, _owc_policy_lang, _owc_policy_manual_edit.
Dopo la generazione, il link viene reiniettato nelle impostazioni corrispondenti
(link_cookie_policy, link_privacy_policy, link_legal_notice, link_terms), e la
pubblicazione di un'informativa privacy aggiorna la pagina di informativa dichiarata in
WordPress.
Due blocchi prima della pubblicazione
Blocco 1 — i campi obbligatori. La generazione viene rifiutata con l'elenco delle chiavi vuote, invece di pubblicare clausole bucate.
| Documento | Campi richiesti |
|---|---|
| Tutti | legal_company_name, legal_company_email |
| Tranne la cookie policy | + legal_company_address, legal_country |
| Note legali | + legal_company_phone, legal_publication_director, legal_host_name, legal_host_address, legal_host_phone |
| Note legali, paese FR / BE / LU | + legal_company_legal_form, legal_company_reg_number |
Blocco 2 — la lingua. Il generatore rifiuta di pubblicare un documento redatto in una lingua che la giurisdizione non usa. In anteprima, il documento esce con una fascia rossa di avvertimento; in pubblicazione, è un rifiuto netto. Nella scheda Policies, il pulsante di generazione è nascosto quando non esiste alcun template per il tipo e il profilo attivi, invece di comparire e fallire sistematicamente.
Risoluzione della lingua
| Profilo | Lingua del documento |
|---|---|
quebec | francese |
lgpd | portoghese |
gdpr con legal_country ∈ {FR, BE, LU} | francese |
| Tutto il resto | inglese |
L'avvertimento obbligatorio
Finché policy_disclaimer è attivo — e lo è di default — ogni documento si chiude con
un blocco che dice che si tratta di un modello generato automaticamente, che deve essere
riletto da un professionista qualificato prima della pubblicazione, e che riporta la data di
generazione e la versione del plugin. Disattivarlo solo con piena consapevolezza.
La tabella dei traccianti — le regole di onestà
La tabella pubblicata nella cookie policy proviene dalla propria tabella di scanner, non da un database di terzi. Cinque regole governano ciò che mostra.
- Finestra di freschezza: vengono pubblicati solo i rilevamenti rivisti negli ultimi 90 giorni. Se la colonna dell'ultima osservazione non esiste ancora perché una migrazione non è passata, la finestra viene ignorata invece di pubblicare «nessun tracciante» su un sito che ne ha — la sotto-divulgazione è l'unica direzione in cui un documento legale non deve mai sbagliare.
- Riquadro di avvertimento in testa alla tabella, derivato dal pannello di copertura dello scanner: scansione mai avviata, nessuna pagina letta, scansione interrotta, N URL fallite, nessuna osservazione con JavaScript attivo, finestra di N giorni. Una scansione incompleta viene dichiarata, non pubblicata come un inventario completo.
- I traccianti non classificati non vengono nascosti: hanno una sezione propria. Sono quelli che nessuno ha esaminato.
- Un cookie osservato in un header
Set-Cookieè marcato con un segno distintivo, con una nota che spiega che è depositato dal server e che nessun blocco lato client può trattenerlo. Non deve quindi essere presentato come condizionato al consenso, e la clausola che afferma che i traccianti non classificati sono depositati solo dopo il consenso porta la relativa eccezione. - Il nome del responsabile e il link alla sua informativa provengono dal catalogo, solo per le regole che puntano a una URL. I pattern di nome di cookie sono esclusi: in un documento legale non si afferma senza prove chi tratta i dati.
I cookie del sito e le durate pubblicate
La sezione «cookie depositati da questo sito» elenca i cookie del plugin stesso:
owc_consent (durata derivata dall'impostazione di rinnovo), euconsent-v2 se TCF è
attivo, owc_geo se il rilevamento regionale è attivo, owc_gpc (durata di sessione),
owc_gpc_notice (5 minuti), più i cookie di accesso e di impostazioni di WordPress.
La tabella delle durate di conservazione trae i propri numeri dalle impostazioni reali —
ledger_retention_days, retention_form_data_days, retention_dsar_days e la durata di
rinnovo del consenso. Nessun valore decorativo viene pubblicato. La durata del cookie di
consenso è derivata dallo stesso calcolo del cookie stesso, il che garantisce che
un'impostazione di 0 mesi pubblichi «13 mesi» e non «mai».
Rilevamento del tipo di attività (condizioni generali)
Le condizioni generali contano 21 sezioni, con commutazione venditore/prestatore, prodotti/servizi, preventivo/ordine, e clausole specifiche per tipo di attività.
legal_business_type (auto, vitrine, rental, ecommerce, services, saas,
content) è la fonte di verità. Quando vale auto, un'euristica esamina il sito —
presenza di WooCommerce, registrazione aperta, plugin di abbonamento, pagina dei prezzi,
pagine di servizi, poi densità di parole chiave sugli ultimi cinquanta contenuti pubblicati
— ma il suo verdetto non viene mai stampato in un documento pubblicato: serve solo a
scegliere le clausole facoltative.
Le citazioni di articoli francesi vengono inserite solo se legal_country vale FR. Il
Belgio ha le proprie citazioni (CDE art. VI.45 §1er, art. VI.47, Servizio di mediazione per
il consumatore); il Lussemburgo e il Québec ricevono una formulazione neutra. Citare un
testo non verificato sarebbe il difetto, non il rimedio.
Altri comportamenti
- La data del documento è formattata nella lingua del documento. Se il pacchetto di
traduzione corrispondente non è installato, il formato passa a
gg/mm/aaaa: un documento francese non può aprirsi con «4 September 2026». - Modifica manuale rilevata: se il contenuto memorizzato non corrisponde più alla sua impronta, viene registrata una revisione prima della sovrascrittura e la schermata mostra l'etichetta «modifiche manuali sostituite», con un link alle revisioni.
- Il generatore non retrocede mai: una pagina pubblicata resta pubblicata anche se la casella «pubblica» viene deselezionata, e un titolo o un permalink rinominato sopravvive a una rigenerazione.
- Iniezione nel menu del footer:
auto_footer_menu_inject, disattivato di default. Il plugin non modifica il sito pubblico senza una richiesta esplicita. Posizioni riconosciute quando lo si attiva:footer,footer-menu,footer_menu,footer-1,footer_1,secondary,legal.
Punti di estensione
// Corpo grezzo del template, con le {{variabili}} ancora al loro posto.
add_filter( 'owc_policy_template', function ( $html, $type, $profile ) {
return $html;
}, 10, 3 );
// Le variabili iniettate nel template.
add_filter( 'owc_policy_vars', function ( array $vars, $type, $profile ) {
return $vars;
}, 10, 3 );
// L'HTML finale, con le variabili sostituite.
add_filter( 'owc_policy_html', function ( $html, array $vars ) {
return $html;
}, 10, 2 );
Fornire il proprio corpo tramite owc_policy_template disattiva il blocco di lingua: un
sito che fornisce il proprio testo se ne assume la lingua. È la via ufficiale per pubblicare
note legali non francesi.
Quattro blocchi pre-generati sono disponibili come variabili riservate:
{{__trackers_table__}}, {{__categories_list__}}, {{__retention_table__}},
{{__jurisdictional_rights__}}. Tutte le variabili sono già sottoposte a escape nel
contesto del loro uso. Tre metodi pubblici sono riutilizzabili da un template di terze
parti: OWC_Policies::build_cookie_table(), build_data_retention_table() e
render_first_party_cookies().
Rotte REST
| Metodo | Percorso | Parametri | Accesso |
|---|---|---|---|
POST | /owc/v1/policies/generate | type (obbligatorio), publish (booleano, predefinito false) | manage_options |
GET | /owc/v1/policies/preview | type (obbligatorio) | manage_options |
L'HTML dell'anteprima passa da wp_kses_post() prima di essere restituito.
Limiti del generatore
- Sono modelli, non consulenza legale. Vanno fatti rileggere.
- Note legali e condizioni generali esistono solo in francese. Qualsiasi altra giurisdizione incontra il blocco di lingua, a meno che non si fornisca il proprio corpo.
- Tre sole lingue: francese, inglese, portoghese.
- Quattro paesi del SEE hanno un'autorità nominata; altrove, una formulazione generica.
- La tabella dei traccianti vale quanto vale la scansione — e il documento lo dice.
- La classe del generatore pesa circa 440 KB di template giuridici: viene caricata in modo pigro, solo quando uno dei tre punti di ingresso viene realmente sollecitato.
Il registro dei consensi
Il registro è la risposta all'articolo 7.1 del GDPR: poter dimostrare che la persona ha prestato il consenso. È attivo di default.
La tabella
{prefix}owc_ledger, tutte le date in UTC:
| Colonna | Tipo | Contenuto |
|---|---|---|
id | bigint | Chiave primaria |
created_at | datetime | Marca temporale UTC |
visitor_token | char(32) | Token pseudonimo del browser, 32 caratteri esadecimali |
event | varchar(20) | accept_all, reject_all, save_preferences, withdraw, auto, gpc_opt_out |
categories | varchar(255) | Elenco delle categorie concesse |
profile | varchar(20) | Profilo di conformità in vigore al momento dell'azione |
source | varchar(60) | banner, preferences, footer_link, api, auto |
ip_pseudonymous | varchar(45) | IP troncato |
ua_hash | char(64) | Impronta con salt dello user-agent |
page_url | varchar(500) | Pagina in cui l'azione è avvenuta |
prev_hash | varchar(128) | Impronta della riga precedente |
row_hash | varchar(128) | Impronta di questa riga |
policies_hash | char(64) | Impronta dei documenti in vigore |
banner_revision | varchar(40) | Impronta del banner realmente mostrato |
plugin_version | varchar(20) | Versione del plugin al momento della scrittura |
Le ultime tre colonne permettono di ricostruire ciò che il visitatore ha visto, non solo ciò che ha spuntato.
La catena
Ogni riga è firmata tramite HMAC su una serializzazione canonica con prefisso di lunghezza del proprio contenuto e dell'impronta della riga precedente. La genesi è una sequenza di 64 zeri. La scrittura avviene in una transazione, con un lock di riga sull'ultima voce: è questo lock a serializzare davvero le scritture concorrenti e a impedire alla catena di biforcarsi. Un lock nominato MySQL viene preso come seconda barriera, best effort: se viene rifiutato, l'aggiunta continua e viene attivata un'azione per poterlo tracciare.
I valori sono troncati prima della firma, perché il valore firmato sia esattamente il valore memorizzato.
L'onestà sull'inalterabilità
Il plugin determina da sé la provenienza della propria chiave di firma:
| Provenienza | Condizione | Verdetto |
|---|---|---|
constant | OWC_LEDGER_KEY è definita | A prova di manomissione |
wp-config | AUTH_KEY e AUTH_SALT sono definite, non vuote, diverse tra loro e senza la frase predefinita | A prova di manomissione |
database | Altrimenti | Non a prova di manomissione |
Nel terzo caso, WordPress memorizza i salt nel database: chiunque abbia accesso al database può ri-firmare la catena. Il plugin lo riporta nel risultato della verifica e mostra un avviso di amministrazione che lo dice a chiare lettere. Viene conservata un'impronta non reversibile della chiave, il che permette di distinguere una rotazione di salt da una riscrittura.
È la ragione per cui la sezione Installazione raccomanda di definire
OWC_LEDGER_KEY.
La verifica
Il pulsante «Verify the chain now» della scheda Audit ledger — o la rotta
GET /owc/v1/ledger/verify — percorre l'intera catena a lotti di 500 righe.
Sono riconosciuti tre schemi di firma: lo schema canonico attuale, uno schema storico precedente, e uno schema senza chiave proveniente dalle primissime versioni. Quest'ultimo produce un'impronta che chiunque possieda il database può ricalcolare: non viene mai considerato valido, è contato a parte e segnalato come una rottura. Un digest senza chiave non è una prova.
Rotture riportate: hash_mismatch, chain_break, bad_genesis, unkeyed_rows,
table_emptied, tail_truncated, head_mismatch, count_mismatch.
Il risultato riporta in particolare: ok, total, checked, table_total, broken_at,
breaks, break_count, partial, legacy_rows, unverifiable_rows, anchor,
anchor_ok, key_source, tamper_evident, key_rotated.
L'ancora di testa
Un'ancora è conservata fuori dalla tabella: identificatore, impronta, numero di righe, marca temporale. Senza di essa, eliminare le righe più recenti o svuotare la tabella non lascerebbe alcuna traccia. Un'azione viene attivata a ogni spostamento dell'ancora, e il codice invita esplicitamente a replicarla fuori dal database — file, syslog, endpoint esterno — per rendere rilevabile un replay globale.
Il plugin rifiuta di sovrascrivere un'ancora esistente e segnala onestamente che un'ancora derivata dalla tabella stessa rende rilevabili solo i troncamenti successivi.
La conservazione
ledger_retention_days, predefinito 1825 giorni (5 anni), limiti da 0 a 3650. Il valore
0 significa conservazione illimitata.
La purga gira sul proprio cron quotidiano e toglie solo un prefisso contiguo: mai un buco in mezzo alla catena. L'impronta dell'ultima riga eliminata viene memorizzata perché il resto resti verificabile. Tetto per esecuzione: 20.000 righe (40 lotti da 500). Un arretrato di purga molto grande si riassorbe quindi nell'arco di più giorni.
Minimizzazione dei dati
- Indirizzo IP: IPv4 con l'ultimo ottetto azzerato (il /24 è conservato), IPv6 troncato al /48 con gli 80 bit restanti a zero. Il mascheramento avviene sulla forma binaria; un indirizzo IPv6 che mappa un IPv4 è trattato come IPv4.
- User-agent: viene memorizzata solo un'impronta con salt, mai la stringa.
page_url: validata contro gli host del sito. Il chiamante fa fede — se fornisce il valore, anche vuoto, quella è la risposta; ilRefererviene consultato solo se il chiamante non ha detto nulla, e viene validato allo stesso modo.- Token visitatore: 32 caratteri esadecimali prodotti da un generatore crittografico, senza alcun legame con un'identità.
L'algoritmo
ledger_hash_algo accetta sha256 (predefinito) e sha3-256, intersecati con gli
algoritmi che il PHP in uso supporta davvero. L'impostazione è mostrata ma non
modificabile in amministrazione: cambiare algoritmo farebbe fallire la verifica di tutte
le righe esistenti. Le colonne sono dimensionate più larghe, ma la scelta reale si limita a
questi due algoritmi.
La scheda Audit ledger
Elenco paginato a 25: identificatore, data locale e marca temporale UTC grezza, token visitatore, evento, categorie, profilo, IP pseudonimizzato, e l'anello di catena (impronta precedente → impronta corrente, troncate nella visualizzazione, valore completo nel tooltip).
Filtri: token visitatore (32 esadecimali), evento, profilo, intervallo di date — inseriti nel fuso orario del sito e confrontati con le marche temporali UTC realmente memorizzate.
Export CSV e JSON. Il JSON porta un involucro (formato, algoritmo, provenienza della chiave) perché un'autorità possa ri-verificare l'estratto senza disporre del resto della catena. Tutte le celle CSV sono neutralizzate contro l'iniezione di formule di foglio di calcolo.
Consultare il registro tramite l'API
GET /owc/v1/ledger accetta page, per_page (da 1 a 200, predefinito 50),
visitor_token, from e to. Gli ultimi tre permettono di rispondere a una richiesta di
accesso (art. 15) senza percorrere tutta la catena. Gli estremi sono letti in UTC, e una
data in formato AAAA-MM-GG viene estesa all'intera giornata. I filtri realmente
applicati sono restituiti nella risposta: un filtro respinto dai controlli non deve
leggersi come «ecco tutto il registro».
Che cosa succede quando il registro rifiuta una scrittura
È il comportamento più importante di tutto il plugin. Se il registro è attivo e la scrittura della riga fallisce, il cookie di consenso viene annullato e la risposta è un 503. Nulla viene memorizzato, nessun tracciante viene liberato.
Il ragionamento è diretto: un consenso che non si può dimostrare non deve essere rivendicato. Vedi Risoluzione dei problemi per come procedere.
Punti di estensione
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 è l'hook da usare per replicare l'ancora fuori dal database.
Limiti del registro
- L'inalterabilità è condizionata, e il plugin lo dice da sé.
- Le righe anteriori alla 1.2.0 portano un digest senza chiave: non vengono mai ri-firmate — la ri-firma permetterebbe a un attaccante di far ri-firmare uno storico falsificato — e impediscono il verdetto «catena intatta» finché restano.
- La conservazione è limitata a 20.000 righe per esecuzione quotidiana.
- Il registro non viene eliminato dal cancellatore di dati personali di WordPress: è una catena di hash, rimuoverla distruggerebbe la prova che esiste per portare, e contiene solo un token pseudonimo e un IP troncato. Un messaggio spiega questa scelta all'interessato.
Il portale dei diritti (DSAR)
Il portale permette a una persona di esercitare i propri diritti da una pagina del sito. È attivo di default, ma compare solo dove si colloca lo shortcode.
Il modulo
[owc_dsar_form]
Attributi:
| Attributo | Predefinito | Ruolo |
|---|---|---|
types | access,rectification,erasure,portability,restrict,object,optout | Tipi proposti, separati da virgole |
title | «Esercitare i miei diritti sui miei dati personali» | Titolo del blocco |
submit_label | «Invia la mia richiesta» | Etichetta del pulsante |
Se dsar_enabled è disattivato, lo shortcode mostra «il portale è disattivato» e la
rotta REST risponde 404: la disattivazione è davvero lato server.
L'elenco richiesto viene intersecato con l'elenco che il server accetta: un'opzione rifiutata dall'endpoint non viene mai proposta al visitatore.
Gli otto tipi di richiesta
access, rectification, erasure, portability, restrict, object, optout,
withdraw.
withdraw è volutamente escluso da ciò che il modulo propone. Revocare il consenso ai
cookie è istantaneo nel pannello delle preferenze; farlo passare da una procedura scritta di
30 giorni renderebbe la revoca più difficile del consenso (art. 7.3 GDPR). Un link sotto il
modulo apre direttamente il pannello delle preferenze per questo caso.
Il filtro owc_dsar_types è la fonte di verità unica: la allowlist dell'endpoint ne deriva.
Il ciclo completo
1. Invio. POST /owc/v1/dsar. Tre protezioni lato server, in quest'ordine:
- Trappola per bot: un campo nascosto compilato comporta un rifiuto 400 con il messaggio generico di un invio malformato — un bot non impara nulla sulla ragione del rifiuto. Lato browser, il modulo mostra addirittura il messaggio di successo senza inviare nulla.
- Attestazione (art. 12.6): la casella «confermo di esercitare un diritto sui MIEI dati personali» deve essere spuntata, e questo è verificato lato server, non solo nel browser. Viene registrata insieme alla richiesta.
- Ritardo minimo di 3 secondi tra la comparsa del modulo e l'invio, lato client.
Limiti di frequenza: 3 all'ora per indirizzo IP, 3 al giorno per indirizzo e-mail indicato — l'indirizzo è scelto dall'attaccante, quindi è quello da limitare — e 30 all'ora per l'intero sito.
La riga viene inserita con lo stato pending, un token di 64 caratteri di cui viene
memorizzata solo l'impronta SHA-256, una data di scadenza del token e un termine di
risposta. L'identificatore non viene restituito: niente enumerazione, niente fuga di
dati sui volumi. La risposta vale {ok, mail_sent, message}, e mail_sent riflette il
fallimento reale dell'invio.
Il DPO non viene notificato in questa fase. Altrimenti un chiamante anonimo farebbe partire due e-mail per invio dal dominio del sito.
2. E-mail di verifica. Il link punta alla home page con il token in chiaro come
parametro. Validità: dsar_token_ttl_days, predefinito 7 giorni, limiti da 1 a 90.
L'e-mail porta un header Reply-To ma mai un From riscritto — riscrivere la busta è
ciò che rompe SPF. L'indirizzo di risposta è dsar_email, altrimenti legal_dpo_email;
mai l'indirizzo di amministrazione del sito, che non deve essere pubblicato.
3. Conferma dell'identità. Il link apre una pagina che non esegue nulla: la conferma avviene tramite un POST protetto da un nonce. È ciò che impedisce a uno scanner di link di posta (Safe Links, URL Defense, anteprima di casella) di confermare un'identità al posto della persona.
La pagina è un documento HTML autonomo, servito fuori dal tema, con noindex, nofollow e
header non memorizzabili in cache. I tentativi sono limitati a 30 all'ora per IP. Le
risposte sono distinte: 404 per un link sconosciuto, 200 per un link già confermato, 410 per
un link scaduto, 403 per un nonce scaduto, 500 per un errore di scrittura, 200 per una
conferma.
4. Che cosa attiva la conferma.
- Lo stato passa a
verified. - Il termine viene ricalcolato a partire dalla verifica. Articolo 12.3: il termine decorre da quando la richiesta è completa, non da un invio mai confermato.
- Il token viene bruciato. La sua impronta è conservata volutamente, perché chi riapre il proprio link legga «già confermato» invece di un 404 che gli chiede di ricominciare da capo. L'uso unico è garantito dal controllo di stato.
- Vengono registrati: la data di conferma, l'IP troncato, il token pseudonimo del browser — unico ponte possibile tra il registro dei consensi e la richiesta — e l'identificatore della richiesta WordPress associata.
- Viene aperta una richiesta WordPress nativa con l'identità già provata, quindi
direttamente nella coda di Strumenti → Esporta / Cancella dati personali. Questo fa sì
che l'esecuzione copra tutti i plugin del sito, non solo OW Consent. Corrispondenza:
accesseportability→ esportazione;erasure→ cancellazione; gli altri tipi non aprono una richiesta nativa. - Il titolare viene notificato a
dsar_emailedsar_notify_email, con ripiego sull'indirizzo di amministrazione se nessuno dei due è valido.
5. Esecuzione. Un pannello sopra la scheda DSAR requests elenca le 20 richieste verificate, ordinate per scadenza, con il numero di giorni rimanenti o di ritardo.
| Azione | Comportamento |
|---|---|
| Scaricare i dati (JSON) | Rifiuto 409 se l'identità non è mai stata confermata. Il file è chiamato dsar-<id>-<AAAAMMGG>.json |
| Cancellare i dati | Offerto solo per una richiesta di tipo erasure, e solo dopo la conferma dell'identità. La richiesta viene chiusa prima della cancellazione, altrimenti il testo libero della richiesta in corso sopravvivrebbe alla propria esecuzione |
| +2 mesi | Proroga dell'art. 12.3, una sola volta, con una motivazione di massimo 500 caratteri. Il termine arretra di 60 giorni, i solleciti vengono riarmati, e l'interessato riceve un'e-mail con la nuova scadenza e le motivazioni |
| Chiudere | Stato resolved o rejected, nota obbligatoria. Impossibile riscrivere una richiesta già chiusa, e impossibile registrare «evasa» su un'identità mai confermata |
| Rinviare il link | Emette un nuovo token, il che invalida il precedente |
Cinque stati: pending, verified, resolved, rejected, expired.
La schermata non carica mai il token né la sua impronta, e non mostra il nome del richiedente.
6. Sorveglianza del termine. Sul cron quotidiano: fino a 200 richieste verificate percorse per scadenza, un sollecito sette giorni prima, un sollecito al superamento, una sola volta ciascuno. Una fascia di amministrazione segnala le richieste fuori termine e quelle a meno di sette giorni.
7. Purga. Gli invii mai verificati vengono eliminati dopo la scadenza del loro token, e
le richieste chiuse vengono eliminate oltre retention_dsar_days. Il conteggio parte dalla
chiusura, non dall'invio.
Il bundle di portabilità
L'esportazione produce un documento in formato ow-consent/dsar-export, con la marca
temporale, il sito, l'interessato, la richiesta e i gruppi di dati. Chiama tutti gli
esportatori registrati del sito, pagina fino a 50 pagine per esportatore e si ferma a
20 secondi. La cancellazione fa la stessa cosa sul fronte dei cancellatori, con gli stessi
limiti.
Filtro: owc_dsar_export_bundle( $bundle, $email, $id ).
Ciò che l'esportazione e la cancellazione coprono dipende quindi dai plugin installati sul sito: un plugin che non registra nessuno di questi hook va trattato a mano.
Integrazione con gli strumenti nativi di WordPress
- Esportatore registrato con la chiave
ow-consent, con due gruppi: le richieste di diritti e il registro dei consensi. - Cancellatore:
- gli invii mai confermati vengono eliminati — nessun valore probatorio, solo dati personali;
- alle richieste chiuse vengono cancellati il testo libero e il nome, conservando la riga
minima (data, tipo, stato) come prova (art. 5.2) fino a
retention_dsar_days; - una richiesta ancora aperta viene conservata, con un messaggio che spiega che deve ricevere risposta prima di essere eliminata;
- il registro dei consensi non viene eliminato, per la ragione spiegata sopra.
- Il plugin alimenta anche la bozza di informativa privacy di WordPress.
L'informativa sotto il modulo
Sotto il modulo, un blocco espandibile riporta l'informativa dell'articolo 13: identità e
indirizzo del titolare, contatto privacy, finalità e base giuridica (art. 6.1.c), dati
raccolti, destinatari e durata di conservazione, termine di risposta e possibilità di
proroga, autorità di controllo competente, link all'informativa. Filtro:
owc_dsar_form_notice( $html, $context ).
L'autorità mostrata proviene dalla derivazione descritta in Gli undici profili di conformità, ed è accettata solo se inizia con una maiuscola: leggere l'impostazione grezza nominerebbe l'autorità francese su un sito sudafricano, indiano, australiano o californiano.
Senza JavaScript, il pulsante di invio è nascosto e un messaggio propone l'indirizzo di contatto — perché il browser non faccia mai un invio nativo che metterebbe l'indirizzo del richiedente nell'URL, e quindi nei log di tutti i server e proxy attraversati.
Impostazioni DSAR
| Impostazione | Predefinito | Limiti |
|---|---|---|
dsar_enabled | true | booleano |
dsar_email | '' | contatto pubblico e destinatario delle notifiche |
dsar_notify_email | '' | destinatario aggiuntivo |
dsar_response_days | 30 | da 1 a 30 — mai più di un mese |
dsar_token_ttl_days | 7 | da 1 a 90 |
retention_dsar_days | 1095 (3 anni) | da 1 a 3650 |
Punti di estensione
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 );
Limiti del portale
- L'identità poggia su un solo fattore: il giro di andata e ritorno via e-mail. Ogni prova aggiuntiva ai sensi dell'art. 12.6 va inserita a mano nelle note della richiesta.
- Un invio e-mail difettoso blocca il ciclo: la risposta espone
mail_sent: falsee il modulo mostra un messaggio che rimanda all'indirizzo di contatto, ma la richiesta restapendinge il termine legale non decorre. - I solleciti di scadenza e la purga dipendono da WP-Cron.
- La tabella delle richieste viene sempre eliminata alla disinstallazione del plugin, qualunque sia l'impostazione. Esportare prima di eliminare.
L'opt-out CCPA «Do Not Sell or Share»
Sotto i profili statunitensi, la legge esige un controllo nominato, visibile, e che esegua davvero l'opt-out.
Due modi per collocarlo
Shortcode, dove si vuole:
[owc_dnsmpi]
[owc_dnsmpi label="Non vendere né condividere le mie informazioni personali" class="mio-link"]
| Attributo | Predefinito |
|---|---|
label | «Do Not Sell or Share My Personal Information» |
class | owc-dnsmpi |
Iniezione automatica nel footer: impostazione ccpa_inject_footer, attiva di
default, resa su wp_footer con priorità 20.
Che cosa fa un clic
Un clic esegue l'opt-out. Non apre un pannello. Il runtime recupera un nonce fresco poi
invia un reject_all con la sorgente footer_link e tutte le categorie facoltative a
false. Il link passa a aria-busy="true" e un'area role="status" mostra lo stato:
«registrazione in corso», poi «il tuo opt-out è stato registrato su questo browser», oppure
il messaggio di errore.
Il codice cita la ragione: secondo i regolamenti attuativi della CCPA (§7026(a)(1)), un link che si limita ad aprire un pannello non è un meccanismo conforme.
Ripiego senza fetch né Promise (WebView vecchia, browser integrato): il clic apre il
pannello delle preferenze con le categorie pubblicitarie già posizionate su off.
Il vincolo della cache
L'HTML servito è identico per tutti. Il link viene quindi reso visibile se il profilo
configurato del sito è ccpa o us_generic; altrimenti viene reso nascosto e rivelato
lato client per i visitatori la cui giurisdizione lo esige, in base al cookie di
rilevamento regionale.
Se il rilevamento regionale è disattivato e il profilo configurato non è statunitense, il blocco non viene proprio stampato: niente markup morto.
Nessun dato di visitatore viaggia nella configurazione JavaScript di questo modulo — solo gli elenchi di profili, mai il booleano risolto.
Le risorse di questo modulo sono stampate in <head>, non nel footer, perché il rifiuto
Consent Mode raggiunga gtag prima che un tag manager si attivi. Il JavaScript è solo in
ES5.
Limiti
- Il controllo con un clic richiede
fetchePromise. - Il link è obbligatorio solo sotto i profili
ccpaeus_generic; sotto qualsiasi altro profilo non viene né mostrato né rivelato. - La rivelazione lato client dipende dal cookie di rilevamento regionale, quindi da JavaScript.
Google Consent Mode v2
Il modulo è attivo di default (gcm_enabled).
I sette segnali
Vengono emessi i sette segnali di Consent Mode v2: ad_storage, ad_user_data,
ad_personalization, analytics_storage, functionality_storage,
personalization_storage e security_storage. Le sei categorie del plugin vi sono
associate a partire da una fonte di verità unica, il che garantisce che il banner, il
blocco e il bootstrap non possano divergere. La tabella è riportata in
Le categorie di cookie.
I valori predefiniti, prima di qualsiasi consenso
La chiamata default è stampata in markup statico, e l'update è calcolato nel
browser: è ciò che mantiene la pagina memorizzabile in cache.
| Segnale | Regime opt-in | Regime opt-out |
|---|---|---|
ad_storage | denied | granted |
analytics_storage | denied | granted |
ad_user_data | denied | granted |
ad_personalization | denied | granted |
personalization_storage | denied | granted |
functionality_storage | denied se il sito espone la categoria functional, altrimenti granted | granted |
security_storage | granted sempre | granted |
I due insiemi sono stampati nella stessa pagina. Il runtime sceglie quale applicare in base al cookie di rilevamento regionale. Senza questo, una pagina messa in cache appiccicherebbe la giurisdizione di un visitatore statunitense a un visitatore europeo.
Le due impostazioni associate
| Impostazione | Predefinito | Effetto |
|---|---|---|
gcm_ads_data_redaction | true | Oscura gli identificatori pubblicitari finché ad_storage è negato |
gcm_url_passthrough | true | Fa transitare gclid / dclid attraverso le URL finché i cookie sono negati |
Da sapere
security_storage non viene mai toccato da un aggiornamento: resta concesso, come prevede
la specifica. La dashboard mostra una scheda che indica se Consent Mode è attivo.
IAB TCF v2.2
Il modulo TCF è disattivato di default, e richiede un identificatore che il plugin non può fornire. Leggere la sezione dei limiti prima di attivarlo.
Attivazione
| Impostazione | Predefinito | Limiti |
|---|---|---|
tcf_enabled | false | booleano |
tcf_cmp_id | 0 | da 0 a 4095 |
tcf_publisher_country | FR | codice ISO a 2 lettere |
tcf_publisher_purposes_li | [] | elenco di finalità |
tcf_special_features | [] | elenco di funzionalità speciali |
Il tetto di 4095 non è arbitrario: il campo CmpId occupa 12 bit nella stringa TCF.
Tre casi in cui il modulo rifiuta di emettere qualsiasi cosa
- Nessun CMP ID (
tcf_cmp_id < 1): niente__tcfapi, niente stringa TC, niente cookieeuconsent-v2, nessuna rotta REST. Un avviso di amministrazione lo spiega. Il CMP ID deve essere assegnato da IAB Europe; il plugin non ne fornisce. Emettere una stringa con un CmpId 0 sarebbe peggio che non emettere nulla. - CMP ID superiore a 4095: stesso rifiuto, con un avviso dedicato. Un valore troncato a 12 bit designerebbe un'altra CMP — un'usurpazione d'identità.
- Nessuna Global Vendor List in cache: lo stub viene stampato ma l'API risponde
cmpStatus: 'error'con una stringa vuota, invece di inventare un numero di versione di elenco.
Una diagnosi viene scritta in un'opzione (disabled, missing_cmp_id, no_gvl, active),
e soltanto in contesto di amministrazione o di cron, mai su una pagina pubblica.
La Global Vendor List
- Fonte:
https://vendor-list.consensu.org/v3/vendor-list.json. - Mai scaricata durante il rendering di una pagina. Se ne occupa un cron quotidiano, con una prima esecuzione cinque minuti dopo l'attivazione; in amministrazione con cache fredda, viene messo in coda un unico recupero.
- Richiesta: 5 secondi di timeout massimo, 2 redirect, risposta limitata a 4 MB. Un corpo che raggiunge il tetto è considerato troncato e rifiutato.
- Vengono conservati solo i campi utili per ciascun fornitore; le stack sono svuotate. Se la serializzazione resta troppo grossa, si applicano due livelli di troncamento: prima le etichette e le URL, poi la riduzione alle sole finalità.
- Conservazione: una settimana in un transient, con stale-if-error — in caso di fallimento, viene conservata l'ultima copia valida.
- Rotta pubblica:
GET /owc/v1/tcf/gvl, limitata a 10 richieste all'ora. Serve la copia in cache con unETage unCache-Control: public, max-age=86400, e gestisce le richieste condizionali. Se non c'è nulla in cache, risponde 503 con unRetry-After: 300— mai un elenco sintetico. Questa rotta non attiva mai una richiesta in uscita.
Dove viene calcolata la stringa TC
Nel browser, non in PHP. Uno stub interamente statico viene stampato in <head> con
priorità 0: iframe locator, implementazione di window.__tcfapi, relay postMessage, e un
encoder di segmento Core in JavaScript. La configurazione pubblicata non contiene alcun
dato di visitatore — è ciò che permette di restare compatibili con una cache di pagina.
Filtro: owc_tcf_stub_config.
Esiste un mirror PHP dell'encoder ma non ha alcun chiamante nel plugin: è riservato alle integrazioni e ai test, con l'avvertimento di non stamparlo mai in HTML memorizzabile in cache.
Che cosa viene codificato
Segmento Core soltanto, in base64url senza padding. La sezione fornitori è codificata come campo di bit o come intervalli: entrambe le dimensioni sono misurate e vince la più piccola. L'indice fornitori trasmesso al browser usa un formato compatto proprietario, con un tetto rigido oltre il quale l'indice viene svuotato (i consensi dei fornitori sono allora persi, la versione dell'elenco è conservata).
Corrispondenza categorie → finalità TCF
| Categoria | Finalità |
|---|---|
necessary | nessuna — fuori dal perimetro TCF, il che garantisce che «Rifiuta tutto» non possa produrre un consenso per la finalità 1 |
functional | 1 |
analytics | 1, 8, 9, 10 |
marketing | 1, 2, 3, 4, 7 |
preferences | 1, 5, 6, 11 |
social | 1 |
Filtro: owc_tcf_purpose_map.
Legittimo interesse dell'editore: vengono accettate solo le finalità 2, 7, 8, 9, 10 e
11, perché TCF v2.2 vieta il legittimo interesse per le finalità 1, 3, 4, 5 e 6. Qualsiasi
altro valore di tcf_publisher_purposes_li viene scartato silenziosamente.
Un fornitore è consentito non appena almeno una delle finalità che dichiara sulla base «consenso» è concessa.
Le funzionalità speciali
Sono supportate solo due voci: 1 — uso di dati di geolocalizzazione precisa e 2 —
analisi attiva delle caratteristiche del dispositivo. Dichiarare in tcf_special_features
quelle effettivamente usate; l'elenco viene ricondotto a questo catalogo.
Compaiono come vere caselle di spunta nel pannello delle preferenze. «Accetta tutto» non le spunta: richiedono un opt-in esplicito a sé stante. «Rifiuta tutto» e «Do Not Sell» le deselezionano. Vivono nella stringa TC stessa, e vengono reidratate alla riapertura del pannello a partire dall'API TCF.
Il livello TCF nel pannello
Viene reso solo se il modulo è attivo, cioè tcf_enabled e un CMP ID utilizzabile. Due
blocchi:
- Finalità: solo quelle raggiungibili tramite una categoria non forzata e nominate nel catalogo dell'elenco fornitori. Un interruttore che tornasse indietro da solo sarebbe peggio di nessun interruttore. Commutare una finalità scrive in tutte le categorie che la dichiarano, poi la visualizzazione viene ri-derivata.
- Funzionalità speciali dichiarate.
Nulla viene codificato nella stringa che non sia stato mostrato.
L'API JavaScript
window.__tcfapi( command, version, callback, parameter );
// comandi: ping, getTCData, getInAppTCData,
// addEventListener, removeEventListener, getVendorList
window.__owcTcfUpdateState( tcData ); // sostituisce i dati e notifica gli ascoltatori
window.__owcTcfRefresh(); // ricalcola ed emette 'useractioncomplete'
window.__owcTcfUiShown(); window.__owcTcfUiHidden();
window.__owcTcfSetSpecialFeatures( [ 1, 2 ] );
Versioni accettate: assente, null, 2, '2', 2.2, '2.2'. Qualsiasi altro valore dà
callback(null, false).
gdprApplies
Deciso lato client. Se il rilevamento regionale è disattivato, il valore è true.
Altrimenti, il paese del visitatore viene confrontato con l'elenco dei 31 codici in cui il
GDPR si applica ai sensi del TCF (27 paesi UE, più IS, LI, NO, GB e CH). Fail closed: un
paese sconosciuto dà true.
Il cookie euconsent-v2
Scritto solo lato client, e solo quando il modulo è pronto e il visitatore ha agito.
Altrimenti il cookie viene cancellato. La sua durata è quella del consenso, limitata a
13 mesi (raccomandazione IAB / CNIL). SameSite=Lax, Secure in HTTPS.
Limiti del modulo TCF — da leggere assolutamente
- Non è una CMP registrata presso IAB Europe. Richiede un proprio CMP ID, e anche con esso la policy TCF impone a una CMP registrata di presentare scelte a livello di finalità e a livello di fornitore. Qui gli interruttori delle finalità seguono le categorie e non c'è alcuna scelta a livello di fornitore: i fornitori hanno motivo di rifiutare questo segnale. La schermata di amministrazione lo dice e parla di «modalità compatibile CMP (non ufficiale)», accettata dalla maggior parte degli SSP in sviluppo ma rifiutata in produzione reale nel SEE. Se un ricavo pubblicitario sotto TCF conta, usare una CMP certificata.
- Solo segmento Core: nessun segmento
disclosedVendors,allowedVendorsnépublisherTC. - Nessuna publisher restriction viene emessa: il contatore corrispondente vale sempre 0 e l'oggetto delle restrizioni è vuoto.
purposeOneTreatmenteuseNonStandardTextsvalgono semprefalse,isServiceSpecificvale sempretrue— non c'è portata globale — e la schermata di consenso vale sempre 0.getVendorListignora il parametro di versione e restituisce sempre l'elenco in cache.- I consensi dell'editore ricopiano le finalità generali: nessuna finalità personalizzata.
- Due sole funzionalità speciali.
- Il modulo richiede un cron funzionante e HTTPS in uscita. Senza elenco in cache,
l'API risponde
cmpStatus: 'error'e la rotta pubblica restituisce 503.
Global Privacy Control
GPC è un segnale inviato dal browser — l'header Sec-GPC: 1 e la proprietà
navigator.globalPrivacyControl. Il plugin lo onora di default (gpc_honor), ma il suo
trattamento dipende dal regime giuridico, ed è questo il punto importante.
Sotto i profili statunitensi: vincolante
Il segnale è trattato come un opt-out universale vincolante sotto i profili ccpa e
us_generic. L'Australia è esplicitamente esclusa: regime opt-out, ma che non riconosce
GPC.
La scrittura lato server avviene solo dopo una catena completa di verifiche, in quest'ordine:
gpc_honorè attivo;- il segnale è presente — l'header
Sec-GPC, con ripiego suX-Sec-GPCperché alcuni proxy e CDN rinominano l'header; conta solo il valore esatto1; - il profilo effettivo è
ccpaous_generic— altrimenti non viene scritto nulla; - si tratta di una semplice consultazione di pagina: niente amministrazione, niente cron,
niente AJAX, niente REST, niente XML-RPC, niente WP-CLI, e il metodo è
GET; - gli header non sono ancora stati inviati — un cookie che non si può posare è una decisione che si riregistrerebbe a ogni richiesta;
- il marcatore di sessione
owc_gpcè assente: una volta per sessione di navigazione; - il visitatore non sembra un bot (user-agent vuoto, oppure contenente
bot,crawl,spider,slurp,monitor,uptime,pingdom,headless,preview,curl/,wget,python-,java/,go-http,okhttp,httpclient,libwww,facebookexternalhit); - la scelta già memorizzata non soddisfa già il segnale — altrimenti si marca soltanto il browser;
- il limite di frequenza condiviso è rispettato: 30 all'ora per IP;
- il browser viene marcato prima della scrittura, perché un fallimento non diventi un ciclo di ripetizione.
L'evento viene registrato come gpc_opt_out con la sorgente auto. L'URL di pagina è
ricostruita a partire dall'URL del sito e dal percorso richiesto — mai dall'header
Host, mai dal Referer.
Portata: tutte le categorie facoltative passano a false; le categorie forzate restano
concesse. È una lettura ampia della nozione di vendita o condivisione.
Trasparenza: se GPC sovrascrive una scelta esplicitamente registrata, un cookie di cinque minuti attiva lato client un avviso in fondo allo schermo che lo spiega, con un pulsante «gestisci le mie preferenze» che apre il pannello, e un pulsante di chiusura.
Sotto i profili GDPR e affini: semplice indizio
Fuori dai regimi opt-out statunitensi, il segnale è trattato come un'indicazione, mai come un consenso:
- l'attributo
data-owc-gpc="1"viene applicato su<html>; - Consent Mode porta
ad_storage,analytics_storage,ad_user_data,ad_personalizationepersonalization_storageadenied; - tutte le categorie facoltative vengono messe a
falsesolo in memoria; givenresta falso, il banner resta visibile, e nulla viene registrato.
Un flag interno impedisce al runtime di annunciare al visitatore che è «già in opt-out» quando né il cookie né il registro lo dicono. La giustificazione è scritta nel codice: GPC non è un segnale giuridicamente riconosciuto sotto GDPR ed ePrivacy, la nLPD svizzera, la LGPD, POPIA, PIPL né la Legge 25.
I due cookie posati da questo modulo
| Cookie | Durata | Ruolo |
|---|---|---|
owc_gpc | sessione | Segna che il segnale è già stato trattato per questo browser |
owc_gpc_notice | 300 s | Attiva l'avviso «GPC ha sostituito la tua scelta» |
Entrambi sono su path=/, SameSite=Lax, Secure in HTTPS, e leggibili da script.
Punto di estensione
add_action( 'owc_gpc_honored', function ( array $categories ) {} );
Il rilevamento regionale
Il rilevamento regionale applica a ciascun visitatore il profilo della sua giurisdizione invece del profilo configurato del sito. È disattivato di default.
| Impostazione | Predefinito |
|---|---|
geo_enabled | false |
geo_default_profile | gdpr |
geo_mmdb_enabled | false |
La cascata di rilevamento
Vince il primo che riesce:
- Cloudflare (
CF-IPCountry) — solo se il sito ha dichiarato di stare dietro Cloudflare; - AWS CloudFront (
CloudFront-Viewer-Country) — stessa condizione; GEOIP_COUNTRY_CODE, la variabile scritta dal server stesso (mod_geoip, ngx_http_geoip), quindi non falsificabile dal client; poi, se è dichiarato un proxy, le varianti in header HTTP;- Database MaxMind locale, se
geo_mmdb_enabledè attivo; - Niente.
Senza una dichiarazione esplicita in wp-config.php, nessun header HTTP di paese viene
creduto: sono falsificabili dal client. Vedi Installazione per le
costanti.
Il codice di paese è validato contro l'allowlist ISO-3166-1 alpha-2 ufficialmente assegnata
(circa 249 codici elencati nel codice). I codici utente o riservati — XX, ZZ, il T1
delle uscite Tor — vengono rifiutati.
Il fail closed
Senza un paese rilevato, il plugin legge geo_default_profile ma retrocede a gdpr ogni
profilo opt-out. Un geo_default_profile impostato su ccpa, us_generic o au viene
quindi ignorato per i visitatori non rilevati — e la schermata di amministrazione propone
solo profili opt-in in questo selettore, invece di offrire una scelta silenziosamente
ignorata.
Il motivo è diretto: senza questa salvaguardia, un sito configurato in inglese americano metterebbe ogni visitatore del SEE in regime opt-out, con traccianti segnalati come «concessi» senza alcun consenso.
Nessun ripiego sulla lingua del sito: la lingua di un sito non dice nulla sul luogo in cui si trova il visitatore.
Il cookie owc_geo
- Durata 24 ore,
path=/,SameSite=Lax,Securein HTTPS, leggibile da script — il bootstrap front ne ha bisogno. - Contenuto: il paese, il profilo, una marca temporale e un codice di autenticazione. Mai un indirizzo IP.
- Alla lettura: dimensione limitata, paese validato contro l'elenco ISO, marca temporale nella finestra, codice verificato a tempo costante. Il profilo è sempre ricalcolato lato server a partire dal paese: un visitatore non può scegliersi il regime giuridico.
- Solo un rilevamento reale viene messo in cache. Il ripiego «fail closed» non viene mai memorizzato.
- Scritto solo su una richiesta front-end, e solo se il rilevamento è attivo.
La corrispondenza paese → profilo
| Paese | Profilo |
|---|---|
| I 30 paesi del SEE | 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 |
| Tutto il resto | Il ripiego, retrocesso a opt-in |
Hong Kong rientra in realtà nella propria ordinanza locale; è trattata come PIPL per
severità. La Nuova Zelanda è trattata come l'Australia. L'intero Canada riceve quebec —
vince il regime più severo. Gli Stati Uniti ricevono ccpa, che fa da profilo anche per gli
altri Stati.
Il vincolo della cache — che cosa il rilevamento non fa
Il profilo geolocalizzato non cambia l'HTML reso. La riscrittura del profilo si applica
solo alle due rotte REST rivolte al visitatore, /owc/v1/consent e /owc/v1/state. Il
rendering di pagina, le rotte di amministrazione, wp-admin, il cron e WP-CLI mantengono il
profilo configurato. Ogni risposta che ha consumato la riscrittura è marcata come non
memorizzabile in cache.
È il runtime front a scegliere il regime, lato client, a partire dal cookie. Conseguenza dichiarata: il regime applicato a un visitatore dipende da JavaScript, e un visitatore la cui primissima pagina arriva dalla cache è trattato sotto il profilo configurato del sito finché il cookie non esiste.
Il lettore MaxMind incorporato
Il plugin incorpora un proprio lettore MMDB, scritto in PHP puro, senza dipendenze Composer — un plugin pubblicato su WordPress.org non può incorporare il pacchetto ufficiale.
- Sola lettura, solo paese. Niente città, niente ASN.
- Input/output limitati: lettura a piccoli blocchi, mai il caricamento in memoria di un file di più megabyte.
- Non solleva mai un'eccezione: file mancante, illeggibile, troncato, corrotto o ostile
dà
null, e il rilevamento ripiega sul proprio profilo severo. - Salvaguardie interne sulla profondità di percorso, sul budget di decodifica e sulla dimensione del payload. I metadati sono messi in cache in un transient la cui chiave integra la dimensione e la data del file: sostituire il download mensile invalida la cache da sé.
registered_countryerepresented_countrysono deliberatamente ignorati. È la forma documentata degli intervalli proxy anonimi, VPN e satellitari, in cui il paese di registrazione designa il luogo di registrazione del blocco da parte del fornitore, non il visitatore. Usarli metterebbe in regime opt-out un visitatore del SEE dietro una VPN statunitense.- Se un altro plugin carica già un lettore GeoIP2, viene usato come ripiego dopo il lettore interno.
Dove mettere il file: wp-content/uploads/ow-geoip/GeoLite2-Country.mmdb. Il percorso è
filtrabile tramite owc_geo_mmdb_path, con validazione contro il path traversal.
Limite importante: il plugin non scarica il database e non offre alcuna schermata di caricamento. Il file va ottenuto da MaxMind, depositato e aggiornato per proprio conto. In compenso, a MaxMind non viene inviato nulla: la ricerca è interamente locale, l'indirizzo IP serve al solo punto di ricerca e viene esplicitamente distrutto dopo — mai registrato nei log, mai memorizzato, mai scritto nel cookie.
Limiti del rilevamento regionale
- Disattivato di default, e la variante MaxMind richiede un file da fornire.
- Gli header di paese sono ignorati finché non si è dichiarato il proprio proxy.
- Nessuna granularità per singolo Stato negli Stati Uniti, né per provincia in Canada.
- Il regime del visitatore dipende da JavaScript e dal cookie di 24 ore.
- Il profilo
us_genericnon viene mai assegnato automaticamente.
L'integrazione OW Forms
Perimetro esatto: questa integrazione riguarda il plugin gemello OW Forms, e nient'altro. Non esiste alcuna integrazione con Contact Form 7, Gravity Forms o WPForms.
Impostazione: forms_integration_enabled, attiva di default, ma l'integrazione non fa
nulla se OW Forms non è attivo.
Che cosa apporta
1. Il collegamento consenso ↔ invio. A ogni invio memorizzato viene scritta una riga in una tabella dedicata a OW Consent — lo schema di OW Forms non viene mai toccato — contenente:
- il token pseudonimo del visitatore (32 esadecimali, mai l'indirizzo e-mail);
- l'identificatore della voce di registro più recente per quel token;
- le categorie concesse e il profilo di conformità in vigore;
- l'impronta completa dei documenti pubblicati in quell'istante;
- la marca temporale del consenso;
- lo stato della casella GDPR del modulo e l'etichetta esatta che portava.
Quest'ultimo punto è il più utile nella pratica: si può dimostrare non solo che la casella era spuntata, ma che cosa c'era scritto accanto.
2. La cascata di cancellazione. Una richiesta di cancellazione verificata delega l'eliminazione alla routine di OW Forms — che elimina anche i file caricati — poi elimina le righe di collegamento. L'integrazione rileva se OW Forms ha già agganciato il proprio listener alla stessa azione, nel qual caso si limita a sfoltire i propri collegamenti.
3. Gli strumenti privacy di WordPress. Vengono registrati un esportatore e un cancellatore, in modo che Strumenti → Esporta / Cancella dati personali copra gli invii di OW Forms — e anche il portale dei diritti di OW Consent, dato che percorre gli stessi hook. L'esportazione riprende le etichette reali dei campi e aggiunge i campi di consenso. Ogni valore esportato è neutralizzato contro l'iniezione di formule di foglio di calcolo e troncato.
4. La conservazione. Sul cron quotidiano, i collegamenti più vecchi di
retention_form_data_days (predefinito 1095 giorni, limiti da 1 a 3650) vengono
eliminati a lotti di 500, con una passata sugli orfani. Vengono toccate solo le righe di
OW Consent: la conservazione degli invii veri e propri appartiene a OW Forms.
Limiti
- Nessun evento di invio viene scritto nel registro concatenato. La riga di collegamento fa riferimento a una voce esistente, non ne crea una — la catena non deve essere scritta dall'esterno.
- Impossibile collegare un invio se OW Forms è configurato per non memorizzare gli invii: non c'è alcuna riga a cui agganciarsi.
- Nessun recupero retroattivo: il collegamento inizia dal primo invio successivo all'aggiornamento.
- La riconciliazione avviene sull'impronta con salt dell'indirizzo calcolata da OW Forms, con ripiego sull'indirizzo in chiaro che OW Forms conserva anch'esso. Non è uno schema a divulgazione zero: l'indirizzo resta in chiaro in OW Forms, perché bisogna poter rispondere alla persona.
- L'integrazione non modifica mai il plugin OW Forms, le sue tabelle né le sue opzioni.
- Se OW Forms è attivo ma troppo vecchio per esporre la propria routine di cancellazione, il cancellatore risponde esplicitamente che gli invii non sono stati toccati.
Il pulsante flottante
Un piccolo pulsante persistente permette di riaprire il pannello delle preferenze in qualsiasi momento. È il requisito dell'articolo 7(3) — la revoca deve restare semplice quanto il consenso — e della delibera CNIL 2020-091, che chiede un meccanismo accessibile da ogni pagina.
Condizioni di esistenza
Richiede entrambe le impostazioni: floating_button_enabled e banner_enabled. Il
pannello delle preferenze e l'API JavaScript vivono nel runtime del banner; senza di esso,
il pulsante sarebbe un controllo che non fa nulla.
Esce anche in amministrazione, su un feed, su robots.txt, e se wp_head non è stato
attivato.
Opzioni
| Impostazione | Valori | Predefinito |
|---|---|---|
floating_button_enabled | booleano | true |
floating_button_position | bottom-left, bottom-right, top-left, top-right | bottom-left |
floating_button_style | pill, icon | pill |
floating_button_label | testo libero | Cookies |
Il pulsante non ha un'impostazione di tema propria: riprende banner_style, perché le
due superfici seguano la stessa palette.
Comportamento e accessibilità
È un vero <button>, quindi accessibile da tastiera. Porta aria-haspopup="dialog" e un
aria-label traducibile ma non configurabile («Manage my cookie choices»): si regola
solo l'etichetta visibile. In modalità icon, l'etichetta è nascosta visivamente ma resta
letta dagli screen reader.
Viene reso con l'attributo hidden; è il JavaScript a decidere di mostrarlo. È visibile
in tutti gli stati tranne quando il banner o il pannello è già a schermo — compreso
subito dopo una scelta sulla pagina corrente.
Un clic apre il pannello delle preferenze. Se l'API del banner non esiste, il pulsante
ripiega sul frammento #owc-preferences, riattivando l'evento a mano se è già il frammento
corrente.
z-index 99990, nascosto in stampa, supporto della modalità a contrasto forzato e di
prefers-reduced-motion, scostamento sotto la barra di amministrazione in posizione alta.
Shortcode
Il plugin dichiara due shortcode, e due soltanto. Né il banner né il pulsante flottante ne hanno: compaiono ovunque o da nessuna parte, pilotati dalle loro impostazioni.
[owc_dsar_form] — il portale dei diritti
[owc_dsar_form]
[owc_dsar_form types="access,erasure,portability" title="I miei diritti" submit_label="Invia"]
| Attributo | Predefinito |
|---|---|
types | access,rectification,erasure,portability,restrict,object,optout |
title | «Esercitare i miei diritti sui miei dati personali» |
submit_label | «Invia la mia richiesta» |
I tipi richiesti vengono intersecati con quelli che il server accetta. Il tipo withdraw
non viene mai proposto dal modulo; vedi Il portale dei diritti.
[owc_dnsmpi] — l'opt-out CCPA
[owc_dnsmpi]
[owc_dnsmpi label="Non vendere né condividere le mie informazioni personali" class="link-footer"]
| Attributo | Predefinito |
|---|---|
label | «Do Not Sell or Share My Personal Information» |
class | owc-dnsmpi |
Un clic esegue l'opt-out; vedi L'opt-out CCPA. Se si lascia attivo
ccpa_inject_footer, il link è già iniettato nel footer e questo shortcode serve solo a
collocarlo altrove.
Riaprire il pannello da un menu o da un link
Non c'è uno shortcode per questo, perché basta un attributo di classe:
<a href="#owc-preferences" class="owc-open-preferences">Gestisci i miei cookie</a>
<button type="button" data-owc-open>Le mie preferenze</button>
<a href="#owc-dnsmpi" data-owc-dnsmpi="1">Non vendere le mie informazioni</a>
È il metodo consigliato per aggiungere una voce «Cookie» al menu del footer.
REST API
Tutte le rotte vivono sotto il namespace owc/v1, alla consueta radice REST
(https://esempio.com/wp-json/owc/v1/…). Tredici rotte in totale: dodici registrate dal
modulo REST, più la rotta dell'elenco fornitori registrata dal modulo TCF.
Rotte pubbliche
Nessuna autenticazione. La loro sicurezza poggia sul controllo di origine, su un token proprio del plugin e sui limiti di frequenza descritti più sotto.
| Metodo | Percorso | Parametri | Ruolo |
|---|---|---|---|
GET | /nonce | action (predefinito wp_rest) | Restituisce un token fresco. Risposta: {nonce, action, header: "X-OWC-Nonce", ttl: 43200}, con no-store e Vary: Cookie |
POST | /consent | event (predefinito save_preferences), source (predefinito banner), categories (obbligatorio), page_url (predefinito ''), cookie_unreadable (booleano, predefinito false) | Registra una scelta, posa il cookie e scrive la riga di registro |
GET | /state | — | Lo stato del solo chiamante: {given, categories, profile, at}. Mai il token visitatore, mai l'IP, mai l'URL di pagina. no-store + Vary: Cookie |
GET | /tcf/gvl | — | Serve la Global Vendor List in cache. ETag, Cache-Control: public, max-age=86400, 304 su richiesta condizionale, 503 + Retry-After: 300 se non c'è nulla in cache |
POST | /dsar | type (obbligatorio), email (obbligatorio, formato e-mail), message (predefinito '', massimo 2000 caratteri) | Deposita una richiesta di esercizio dei diritti. Risposta {ok, mail_sent, message} — mai l'identificatore |
Valori accettati:
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: un oggetto, massimo 32 voci, solo valori scalariaction(nonce):wp_restsoltanto
Rotte di amministrazione
Tutte richiedono la capability manage_options.
| Metodo | Percorso | Parametri | Ruolo |
|---|---|---|---|
GET | /ledger | page (≥1, predefinito 1), per_page (1–200, predefinito 50), visitor_token (32 esadecimali), from, to | Legge il registro. I filtri realmente applicati sono restituiti nella risposta |
GET | /ledger/verify | — | Verifica la catena da un capo all'altro |
GET | /settings | — | Le impostazioni effettive |
POST | /settings | corpo JSON libero | Scrive le impostazioni. Risposta {ok, updated, rejected, settings} |
POST | /scanner/run | — | Avvia una scansione |
POST | /scanner/ingest | corpo JSON {page, findings[]} | Ricezione delle osservazioni della sonda |
GET | /scanner/findings | page, per_page (1–200, predefinito 50), filter (slug di categoria o unknown) | Elenca i rilevamenti |
POST | /policies/generate | type (obbligatorio), publish (booleano, predefinito false) | Genera un documento |
GET | /policies/preview | type (obbligatorio) | Mostra l'anteprima di un documento |
/scanner/ingest richiede inoltre un nonce wp_rest valido, nell'header X-WP-Nonce
o come parametro _wpnonce — perché sendBeacon non può impostare header.
/settings in scrittura accetta solo le chiavi dichiarate come impostazioni reali: un
filtro può iniettare chiavi sintetiche che non lo sono. Ogni valore è sanificato
individualmente. Nessuna chiave nota dà un 400.
/ledger riverifica la capability nel gestore, come difesa in profondità contro un
permission_callback filtrato altrove.
Il modello di sicurezza delle scritture pubbliche
La constatazione di partenza è semplice: un nonce non può vivere in HTML memorizzabile in
cache. /consent e /dsar hanno quindi un controllo di accesso permissivo, protetto da
quattro livelli in un ordine che conta.
- Controllo di origine stesso-host. Gratuito, deterministico, non consuma alcun budget.
- Token proprio del plugin nell'header
X-OWC-Nonce— è una prova, mai un veto. La scelta di non usareX-WP-Nonceè deliberata: il core di WordPress intercetta questo header prima di qualsiasi controllo di accesso di rotta e rifiuta l'intera richiesta se non lo convalida. Un token scaduto servito da una cache ucciderebbe quindi una scrittura che l'endpoint avrebbe accettato anche senza alcun token. - Fail closed: né origine, né token verificato danno un 403
owc_missing_origin. - Limite di frequenza per ultimo. Se passasse per primo, una configurazione errata consumerebbe due token per clic e finirebbe in 429, il che maschererebbe l'errore di configurazione dietro un limite di frequenza.
Host accettati: quelli che WordPress dichiara (URL del sito, URL di WordPress, radice
REST) con il loro gemello www./apex, più l'host dell'header Host della richiesta
corrente e il suo gemello. «Stessa origine» significa «l'origine corrisponde all'host a cui
il browser si è connesso», non «corrisponde a ciò che WordPress ha nel database» —
altrimenti ogni sito raggiunto tramite un dominio di anteprima, un alias, un nome di
staging, un dominio mappato in multisito o dietro un proxy che riscrive Host verrebbe
rifiutato in silenzio, senza possibilità di autoriparazione. Filtro:
owc_allowed_request_hosts.
CORS è lasciato al core di WordPress, deliberatamente.
/nonce rifiuta esplicitamente la forma JSONP: senza questo rifiuto, una pagina di terze
parti potrebbe raccogliere il token vivo di un amministratore di passaggio con un semplice
<script src>, non soggetto al controllo di origine.
I limiti di frequenza
| Compartimento | Budget | Finestra | Chiave |
|---|---|---|---|
nonce | 120 | 5 minuti | indirizzo IP |
consent | 30 | 5 minuti | indirizzo IP |
dsar | 3 | 1 ora | indirizzo IP |
dsar_email | 3 | 24 ore | indirizzo e-mail indicato |
dsar_global | 30 | 1 ora | intero sito |
gpc | 30 | 1 ora | indirizzo IP |
tcf_gvl | 10 | 1 ora | indirizzo IP |
- Finestra fissa, non scorrevole: un compartimento che riceve traffico finisce sempre per scadere.
- La chiave memorizzata è un'impronta con salt: nessun indirizzo IP né indirizzo e-mail in chiaro si trova nelle opzioni o nell'object cache.
- Normalizzazione dell'IP: IPv4 conservato, IPv6 troncato al /64 — il client controlla ogni bit dell'identificatore di interfaccia, quindi una chiave completa permetterebbe di creare un budget nuovo a ogni richiesta.
- Identità condivisa: dietro una CDN o un proxy non dichiarato tramite
OWC_TRUSTED_PROXY, tutti i visitatori arrivano con lo stesso indirizzo. Il budget viene allora frazionato per visitatore, con un contatore allargato sull'indirizzo condiviso. È un limite più grossolano: dichiarare il proprio proxy. - Filtro
owc_throttle_max( $max, $bucket, $window )— restituire0disattiva il limite. - Il limitatore fallisce in apertura se l'object cache non è disponibile: meglio non limitare che impedire a qualcuno di esercitare i propri diritti.
La rotta /consent nel dettaglio
headers_sent()viene letto per primissimo. Se l'output è già iniziato, il cookie lato server non può essere posato: la riga viene comunque scritta e la risposta resta 200, concookie_set: falsee un descrittorecookie(name,value,ttl,path,samesite,secure) che il client posa da sé.- Le categorie che il sito non offre vengono restituite in
dropped_categories, non ignorate in silenzio. page_urlè validata contro gli host del sito, con ripiego su unReferervalidato allo stesso modo, altrimenti la stringa vuota. La chiave è sempre presente, perché il registro non vada a cercare ilReferergrezzo.- Se il registro è attivo e la scrittura viene rifiutata, il cookie viene annullato e la
risposta è un 503
owc_ledger_write_refused. Nulla viene memorizzato, nessun tracciante viene liberato. cookie_unreadableè pura telemetria: non le viene applicato alcun controllo di validazione, proprio perché un campo diagnostico non possa mai rifiutare una scrittura di consenso. Il suo unico effetto è una nota nel registro dei rifiuti.
Risposta tipo: {ok, state, dropped_categories, logged, cookie_set[, cookie]}. Il campo
logged vale null quando il registro è disattivato.
Codici di errore
| Codice | HTTP | Significato |
|---|---|---|
owc_bad_param | 400 | Parametro non valido |
owc_bad_categories | 400 | Oggetto categories malformato, troppo grande o non scalare |
owc_bad_email | 400 | Indirizzo e-mail non valido |
owc_bad_dsar | 400 | Invio DSAR malformato (trappola per bot inclusa) |
owc_dsar_not_attested | 400 | Attestazione dell'art. 12.6 assente |
owc_no_settings | 400 | Nessuna chiave di impostazione nota nel corpo |
owc_missing_origin | 403 | Né origine né token: fail closed |
owc_bad_origin | 403 | L'origine dichiarata non è un host di questo sito — la risposta elenca fino a dieci host accettati |
owc_bad_nonce | 403 | Nonce non valido su /scanner/ingest |
owc_jsonp_forbidden | 403 | Forma JSONP rifiutata su /nonce |
owc_forbidden | 401/403 | Capability insufficiente |
owc_dsar_disabled | 404 | Il portale dei diritti è disattivato lato server |
owc_no_template | 404 | Nessun template per questo tipo e questo profilo |
owc_rate_limited | 429 | Limite di frequenza raggiunto |
owc_consent_failed | 500 | Errore nell'applicazione del consenso |
owc_dsar_store_failed | 500 | Errore di scrittura della richiesta |
owc_gen_failed | variabile | Generazione di documento rifiutata (campi mancanti, barriera di lingua) |
owc_ledger_write_refused | 503 | Il registro ha rifiutato di scrivere — nulla viene registrato, nulla viene sbloccato |
Il registro dei rifiuti
Gli ultimi venti rifiuti di scrittura vengono conservati e mostrati sulla dashboard — solo se il registro non è vuoto. Vi si distinguono due nature:
- una voce con uno stato HTTP è un vero rifiuto: nulla è stato memorizzato e il visitatore ha visto un errore;
- una voce con stato 0 è un avvertimento: la scelta è stata comunque registrata.
Il pannello mostra affiancati l'host dichiarato dall'origine e l'header Host della
richiesta. È esattamente ciò che differisce quando i visitatori navigano su un host mentre
WordPress è configurato con un altro — la causa più frequente di un consenso che «non si
registra».
Limiti dell'API
/consente/dsaraccettano scritture anonime per progettazione; la protezione è l'origine più il limite di frequenza, non un nonce.- Il limitatore di frequenza fallisce in apertura senza object cache.
- Il cookie di consenso è host-only: un cookie posato per l'apex è illeggibile da una
pagina
www., e nessun header CORS può correggere questo. Il rimedio è una URL di scrittura della stessa origine. /scanner/ingestaccetta solomanage_options: la sonda non può provenire da un visitatore./settingsnon ha un nonce proprio: sonomanage_optionspiù il controllo di nonce tramite cookie del core.
Riferimento delle impostazioni
Tutte le impostazioni stanno in un'unica opzione, owc_settings, caricata
automaticamente dato che viene letta a ogni pagina del front.
Lettura e scrittura in PHP
$impostazioni = OWC_Core::settings(); // impostazioni effettive (default + salvate + filtri)
$profilo = OWC_Core::setting( 'compliance_profile' );
$grezzo = OWC_Core::stored_setting( 'compliance_profile' ); // ignora la riscrittura per visitatore
OWC_Core::update_settings( array(
'consent_renewal_months' => 6,
'compliance_strict' => true,
) );
update_settings() fonde solo le chiavi inviate sopra l'esistente, mai l'insieme dei
valori predefiniti. È ciò che permette alla riga di opzione di restare vuota finché non si è
personalizzato nulla, e quindi ai testi di seguire la lingua del sito.
Il risultato di settings() è memoizzato, con una chiave fondata sull'insieme delle
callback del filtro owc_settings: un modulo che registra il proprio filtro dopo la prima
lettura invalida il memo invece di essere ignorato. Il memo viene svuotato a ogni scrittura
dell'opzione.
Valori predefiniti
[
// 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, // X = rifiuto implicito
'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',
// Pulsante flottante
'floating_button_enabled' => true,
'floating_button_position' => 'bottom-left',
'floating_button_style' => 'pill',
'floating_button_label' => 'Cookies',
// Consenso
'consent_renewal_months' => 12, // 0 viene convertito in 13, non in «mai»
'consent_policy_hash_check' => true,
// Conformità
'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
'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.',
// Registro
'ledger_enabled' => true,
'ledger_retention_days' => 1825, // 5 anni ; 0 = illimitato
'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',
// Portale dei diritti
'dsar_enabled' => true,
'dsar_email' => '',
'dsar_response_days' => 30,
'dsar_notify_email' => '',
'dsar_token_ttl_days' => 7,
// Blocco
'blocker_unknown_script_policy' => 'allow',
'blocker_unknown_iframe_policy' => 'block',
'blocker_allowlist' => '', // un host per riga
'blocker_block_resource_hints' => true,
// Integrazione e ciclo di vita
'forms_integration_enabled' => true,
'auto_footer_menu_inject' => false,
'delete_data_on_uninstall' => false,
// Identità legale
'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' => '', // vuoto di proposito: derivata da paese + profilo
'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,
// Link
'link_privacy_policy' => '',
'link_cookie_policy' => '',
'link_terms' => '',
'link_legal_notice' => '',
]
Elenchi chiusi
Un valore fuori elenco viene riportato al predefinito.
| Impostazione | Valori accettati |
|---|---|
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 (intersecati con ciò che PHP supporta) |
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 |
Limiti degli interi
| Impostazione | Limiti |
|---|---|
consent_renewal_months | da 0 a 13 (0 è trattato come 13) |
dsar_response_days | da 1 a 30 |
dsar_token_ttl_days | da 1 a 90 |
ledger_retention_days | da 0 a 3650 (0 = illimitato) |
scanner_max_urls | da 1 a 500 |
scanner_timeout | da 1 a 60 |
legal_minor_age | da 13 a 18 |
retention_form_data_days | da 1 a 3650 |
retention_dsar_days | da 1 a 3650 |
tcf_cmp_id | da 0 a 4095 |
Sanificazione
text_messageè l'unico campo con HTML ricco; accetta l'HTML consentito in un articolo WordPress, e il banner rende in definitiva solo<a href target rel>.- Campi multiriga:
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, e qualsiasi chiave che termina con_desc. - URL: qualsiasi chiave con prefisso
link_o suffisso_url, piùlegal_profession_rules. - Indirizzi e-mail: qualsiasi chiave che contiene
email. - Tutto il resto: testo semplice.
- Un valore non scalare inviato su un'impostazione scalare viene ignorato, la chiave non viene scritta.
Dove si regola che cosa
| Scheda | Impostazioni |
|---|---|
| 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 (l'algoritmo è mostrato, non modificabile) |
| Scanner | scanner_* |
| DSAR requests | dsar_* |
Tabelle
| Tabella | Contenuto |
|---|---|
{prefix}owc_ledger | Registro dei consensi concatenato |
{prefix}owc_dsar | Richieste di esercizio dei diritti |
{prefix}owc_scanner | Rilevamenti dello scanner |
{prefix}owc_scripts | Catalogo di firme del blocco |
{prefix}owc_form_links | Collegamenti consenso ↔ invio OW Forms |
Un helper PHP owc_table( 'ledger' | 'dsar' | 'scanner' | 'scripts' ) ricalcola il nome a
partire dal prefisso corrente: usarlo dentro uno switch_to_blog(), dato che le
costanti restano congelate per la durata della richiesta.
Altre opzioni e transient
Opzioni: 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, più i flag di migrazione
(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).
Transient: owc_catalog_<versione>, owc_invalid_patterns, owc_gvl_cache,
owc_gvl_stub, owc_gvl_etag, owc_gvl_retry, owc_mmdb_<impronta>,
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, e i contatori di limitazione di frequenza.
Costanti riconosciute in wp-config.php
| Costante | Effetto |
|---|---|
OWC_LEDGER_KEY | Chiave HMAC del registro, e chiave derivata del cookie di rilevamento regionale |
OWC_TRUSTED_PROXY / OWC_TRUSTED_PROXIES | Intervalli CIDR dei proxy attendibili; senza di esse, gli header di IP client sono ignorati |
OWC_GEO_TRUSTED_HEADERS | cloudflare, cloudfront, proxy o all |
OWC_BEHIND_CLOUDFLARE | Scorciatoia per l'header di paese di Cloudflare |
OWC_BEHIND_CLOUDFRONT | Scorciatoia per l'header di paese di CloudFront |
Gli hook per sviluppatori
Filtri
| Filtro | Ruolo |
|---|---|
owc_settings | Impostazioni effettive. La risposta deve dipendere da qualcosa di stabile per tutta la richiesta |
owc_banner_texts | Testi del banner |
owc_bootstrap_config | Configurazione statica stampata in <head> — mai un dato per visitatore |
owc_scripts_catalog | Righe del catalogo prima della validazione |
owc_blocker_allowlist | Host mai bloccati |
owc_scanner_urls | URL da scansionare (poi nuovamente ristrette all'host del sito) |
owc_throttle_max | Budget di un limite di frequenza; 0 lo disattiva |
owc_allowed_request_hosts | Host accettati per una scrittura pubblica |
owc_allow_headerless_write | Solleva il fail closed sulle scritture senza origine (predefinito false) |
owc_geo_mmdb_path | Percorso del database MaxMind |
owc_tcf_purpose_map | Corrispondenza categorie → finalità TCF |
owc_tcf_stub_config | Configurazione statica dello stub TCF |
owc_policy_template | Corpo grezzo di un template — disattiva il blocco di lingua |
owc_policy_vars | Variabili di un documento |
owc_policy_html | HTML finale di un documento |
owc_dsar_types | Tipi di richiesta proposti e accettati |
owc_dsar_form_notice | Informativa sotto il modulo |
owc_dsar_export_bundle | Contenuto del bundle di portabilità |
owc_dsar_show_fulfilment_panel | Visualizzazione del pannello di esecuzione |
Azioni
| Azione | Firma |
|---|---|
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 ) |
Cron: owc_daily_maintenance, owc_ledger_retention, owc_scanner_run,
owc_scanner_run_batch, owc_tcf_refresh_gvl, owc_run_upgrade.
Internazionalizzazione
Il dominio di traduzione è ow-consent, il percorso /languages. La lingua sorgente è
l'inglese. Il pacchetto include il template ow-consent.pot e una traduzione francese
completa.
Promemoria del meccanismo descritto sopra: finché un'etichetta o un testo del banner non viene personalizzato nelle impostazioni, segue la lingua del sito. Appena si inserisce un valore proprio, questo viene servito così com'è, qualunque sia la lingua del visitatore.
I corpi dei documenti legali non passano da questo meccanismo: la loro lingua segue la giurisdizione, mai la lingua dell'amministratore.
Risoluzione dei problemi
Il banner non compare
Percorrere l'elenco nell'ordine:
banner_enabledè attivo? Il badge in cima alla schermata di amministrazione lo dice.- Il tema chiama
wp_head()ewp_footer()? Senza di essi, il banner rifiuta di stamparsi invece di produrre un markup inerte. - Il visitatore ha già fatto una scelta? Il banner compare solo su
<html data-owc="none">. Provare in navigazione privata o conwindow.OWCBanner.reset(). - Il visitatore ha JavaScript? Senza di esso il banner resta nascosto — di proposito —
e subentra il blocco
<noscript>. - Una cache di pagina sta servendo una versione anteriore all'attivazione? Svuotarla.
Il banner ritorna a ogni pagina anche se la scelta è stata registrata
Tre cause, in ordine di frequenza.
- L'host visitato è diverso dall'host configurato in WordPress (
www.contro apex, alias, dominio di staging). Il browser rifiuta allora di memorizzare il cookie mentre WordPress risponde 200. Aprire la dashboard: il registro dei rifiuti mostra affiancati l'origine e l'headerHost. Il rimedio è servire il sito su un solo host canonico. consent_policy_hash_checkè attivo e i documenti sono cambiati — è il comportamento voluto, il visitatore viene risollecitato dopo una modifica dell'informativa.- Si sta usando una versione anteriore alla 1.4.3. Il cookie era allora codificato due volte e il browser non riusciva a rileggerlo, mentre il server lo leggeva benissimo. Aggiornare: i cookie scritti prima della correzione restano leggibili.
«La tua scelta non è stata registrata» — errore refused o 403
Guardare data-owc-code sull'elemento del messaggio, o il registro dei rifiuti della
dashboard.
owc_bad_origin: l'origine dichiarata non è un host riconosciuto. La risposta elenca gli host accettati. Se il sito è legittimamente raggiungibile con più nomi, aggiungerli con il filtroowc_allowed_request_hosts.owc_missing_origin: né origine, né token. È tipicamente un'estensione per la privacy che rimuove gli header, o un proxy che li riscrive.owc_bad_nonce: riguarda solo la sonda dello scanner, non la scrittura del consenso — il nonce non è mai un veto su/consent.
Errore ratelimit — 429
I limiti sono volutamente bassi sulle scritture pubbliche. Due cause:
- Una CDN o un proxy non dichiarato: tutti i visitatori arrivano con lo stesso
indirizzo e condividono lo stesso budget. Dichiararlo con
OWC_TRUSTED_PROXYinwp-config.php. - Un test automatizzato che invia più di 30 scritture in 5 minuti dallo stesso indirizzo.
Un 503 owc_ledger_write_refused
Il registro ha rifiutato di scrivere, quindi nulla è stato registrato e nessun tracciante è stato liberato. È il comportamento voluto: un consenso che non si può dimostrare non deve essere rivendicato.
- Verificare che la tabella
{prefix}owc_ledgeresista. La scheda Audit ledger mostra un messaggio leggibile «non ancora installato» invece di una schermata bianca. - Verificare i permessi di scrittura dell'utente MySQL.
- Come ultima risorsa, disattivare temporaneamente
ledger_enabledper ripristinare il servizio — sapendo che nel frattempo si perde la prova.
Alcuni traccianti si caricano ancora prima del consenso
compliance_strictè attivo? Senza di esso vengono trattati solo gli script messi in coda da WordPress.- Il tracciante è a catalogo? Uno script di terze parti sconosciuto è autorizzato di default. Avviare una scansione, classificare il rilevamento: questo scrive la regola di blocco.
- È iniettato da JavaScript first-party? La protezione dinamica copre solo venti host. Aggiungere una regola di catalogo sul dominio del tracciante.
- È un cookie posato tramite header
Set-Cookie? Nessun blocco lato client può trattenerlo. Va trattato alla fonte, lato server. - Portare
blocker_unknown_script_policysublock— e poi testare accuratamente il sito, perché questa impostazione blocca ogni terza parte non riconosciuta.
Un'integrazione legittima viene bloccata
Aggiungere il suo host in blocker_allowlist, uno per riga, oppure tramite il filtro
owc_blocker_allowlist. Verificare anche blocker_unknown_iframe_policy: le iframe
sconosciute sono bloccate di default, è il caso più frequente.
Se l'integrazione deve restare condizionata al consenso, conviene piuttosto classificarla nella categoria giusta: il sostituto visivo porta un pulsante «Accetta …» che la sblocca con un clic.
Lo scanner non trova nulla
- È attivo? È disattivato di default.
- WP-Cron funziona? Con
DISABLE_WP_CRONe senza cron di sistema, la scansione pianificata non parte mai. Avviarne una a mano per verificare. - Il loopback HTTP è possibile? Un'autenticazione HTTP di staging o un firewall blocca la scansione. Il pannello di copertura mostra il primo errore.
- Nessun JavaScript viene eseguito dalla scansione lato server. Visitare una pagina del sito da amministratore connesso perché la sonda riporti ciò che un tag manager inietta.
In ogni caso, leggere il pannello «che cosa copre realmente questa scansione» prima di trarre conclusioni: distingue «non ho trovato nulla» da «non ho letto nulla».
La generazione di un documento viene rifiutata
Due cause possibili, e il messaggio lo dice:
- Alcuni campi obbligatori sono vuoti — viene mostrato l'elenco delle chiavi mancanti. Completare la scheda Legal identity.
- Barriera di lingua — si stanno chiedendo note legali o condizioni generali per una
giurisdizione non francofona. In questo caso il pulsante è nascosto. L'unica via è
fornire il proprio corpo tramite il filtro
owc_policy_template.
L'e-mail di verifica DSAR non arriva
È quasi sempre la deliverability, non il plugin.
- La risposta dell'API porta
mail_sent: falsequando l'invio è fallito, e il modulo mostra allora un messaggio dedicato che rimanda all'indirizzo di contatto. - Installare un plugin SMTP. La funzione
mail()di PHP viene rifiutata dalla maggior parte dei server di ricezione. - Verificare SPF, DKIM e DMARC del proprio dominio. Il plugin non riscrive mai
l'indirizzo del mittente — è ciò che romperebbe SPF — imposta solo un
Reply-To. - Verificare che
dsar_emailsia un indirizzo valido.
Finché l'e-mail non viene ricevuta e confermata, la richiesta resta pending e il termine
legale non decorre.
Il modulo TCF non si attiva
Aprire la scheda Compliance: un avviso di amministrazione nomina la causa.
missing_cmp_id:tcf_cmp_idvale 0. Bisogna ottenere un CMP ID presso IAB Europe; il plugin non ne fornisce e rifiuta di emettere una stringa con un identificatore 0.- CMP ID superiore a 4095: il valore non sta nel campo da 12 bit della stringa e designerebbe un'altra CMP.
no_gvl: la Global Vendor List non è ancora stata scaricata. Arriva tramite un cron quotidiano; verificare che WP-Cron giri e che il server possa raggiungerevendor-list.consensu.orgin HTTPS uscente.
Tutti i visitatori ricevono il profilo configurato, nonostante il rilevamento regionale
geo_enabledè attivo? È disattivato di default.- È stato dichiarato il proxy? Senza
OWC_BEHIND_CLOUDFLARE,OWC_BEHIND_CLOUDFRONToOWC_GEO_TRUSTED_HEADERSinwp-config.php, gli header di paese sono ignorati, perché sono falsificabili. - Il file MaxMind è al suo posto? Il plugin non lo scarica e non offre una schermata di caricamento.
- Potrebbe essere normale: la prima visualizzazione di un visitatore servita dalla cache usa il profilo configurato finché il cookie di 24 ore non esiste. La dashboard mostra un blocco diagnostico con il paese rilevato, il profilo applicato e la fonte.
L'amministrazione segnala che la chiave del registro vive nel database
È l'avviso notice_ledger_key. Significa che AUTH_KEY e AUTH_SALT non sono in
wp-config.php — WordPress li memorizza allora nel database — e che nemmeno
OWC_LEDGER_KEY è definita. La catena continua a essere costruita, ma chiunque abbia
accesso al database può ri-firmarla: l'affermazione di inalterabilità non regge più.
Il rimedio è aggiungere OWC_LEDGER_KEY (e, già che ci si è, AUTH_KEY e AUTH_SALT) in
wp-config.php. La verifica distingue poi una rotazione di chiave da una riscrittura,
quindi questo cambiamento non invalida lo storico.
Il plugin non fa proprio nulla, con un avviso rosso
Il WordPress in uso è anteriore alla 6.2. Il plugin rifiuta di avviarsi, e l'avviso dice esplicitamente che nulla è bloccato e che nessun consenso viene registrato. Aggiornare WordPress, oppure disattivare il plugin e togliere il banner dalle pagine nel frattempo.
Alcune regole di catalogo «non possono scattare»
Un avviso di amministrazione le segnala, e la scheda Tracker catalogue ha un filtro dedicato. Quattro motivi possibili: pattern vuoto o composto solo da caratteri invisibili, espressione regolare che non compila o che fa backtracking catastrofico, pattern troppo corto e senza punto per puntare a una URL, oppure categoria che non esiste più sul sito. Correggere o eliminare queste righe: danno una falsa impressione di copertura.
FAQ
Il plugin funziona dietro una cache di pagina? Sì, e tutta la sua architettura è costruita attorno a questo vincolo. L'HTML prodotto è identico per tutti i visitatori; il consenso viene letto nel browser e applicato prima del primo rendering. Nessun nonce viene stampato in HTML memorizzabile in cache. Solo le risposte realmente personali sono marcate come non memorizzabili.
Sostituisce una CMP a pagamento? Sulla maggior parte dei siti copre lo stesso terreno: banner, blocco, log di audit, richieste di diritti, instradamento geografico e documenti generati. Due cose che non fa: non è una CMP registrata presso IAB Europe, e non fornisce consulenza legale né documenti già revisionati — il generatore produce bozze che vanno fatte validare.
Il registro è inalterabile?
È a prova di manomissione, a una condizione. Ogni riga è firmata tramite HMAC
sull'impronta della precedente: una riga modificata o eliminata rompe la catena e la
verifica dice dove. La garanzia poggia sul fatto che la chiave di firma vive fuori dal
database. È il caso se AUTH_KEY e AUTH_SALT sono in wp-config.php, o se si definisce
OWC_LEDGER_KEY. Altrimenti WordPress memorizza i salt nel database e un attaccante che vi
accede potrebbe ri-firmare la catena — il plugin rileva questa situazione e avvisa.
Lo scanner rallenta il sito? No. È disattivato di default, e una volta attivo gira su WP-Cron alla frequenza scelta, recuperando un campione delle pagine del sito lato server. Non viene mai eseguito durante la visita di un visitatore.
Lo scanner trova i cookie?
In parte, ed è importante capirlo. La scansione lato server legge le intestazioni
Set-Cookie delle pagine: trova quindi i cookie posati dal server, compresi gli HttpOnly.
Non esegue alcun JavaScript, quindi i cookie scritti da script nel browser non le sono
visibili; quelli vengono raccolti da una sonda che gira solo per un amministratore connesso.
Il risultato va trattato come un inventario di ciò che è stato visto, non come un elenco
esaustivo — entrambe le schermate lo dicono, invece di lasciar supporre il contrario.
Come viene evasa una richiesta di diritti? La persona invia il modulo e riceve un'e-mail di verifica. Confermare la richiesta sulla pagina collegata fa decorrere il termine dell'articolo 12.3 e apre una richiesta WordPress nativa. Dalla schermata DSAR si scarica un export JSON prodotto da tutti gli esportatori registrati sul sito, si lanciano tutti i cancellatori, e si chiude con una risposta scritta che resta allegata alla richiesta. Ciò che l'export copre dipende quindi dai plugin installati; quelli che non registrano nessuno di questi hook vanno trattati a mano.
Posso usare il modulo TCF per AdSense o Ad Manager? Solo con un proprio CMP ID rilasciato da IAB Europe, e anche in quel caso con una riserva. Senza CMP ID il modulo non si carica affatto. Con un CMP ID emette una stringa TC correttamente codificata e il pannello mostra ogni finalità e ogni funzionalità speciale che quella stringa può rivendicare — ma la policy TCF esige da una CMP registrata scelte a livello di finalità e a livello di fornitore. Qui gli interruttori delle finalità seguono le categorie e non c'è alcuna scelta di fornitore: non è una CMP registrata, e i fornitori hanno motivo di rifiutarne il segnale. Se un ricavo pubblicitario sotto TCF conta per il sito, conviene usare una CMP certificata.
In quale lingua vengono generati i documenti? L'informativa privacy e la cookie policy esistono per tutti gli undici profili, nella lingua della giurisdizione: inglese, francese per Francia, Belgio, Lussemburgo e Québec, portoghese per il Brasile. Le note legali e le condizioni generali esistono solo in francese; per qualsiasi altra giurisdizione il generatore rifiuta di produrle invece di pubblicare un documento inadatto.
Che cosa si può cambiare nel banner?
Quattro posizioni, un tema chiaro, scuro o automatico, un logo facoltativo, le etichette, e
un pulsante flottante di riapertura. Qualsiasi elemento con la classe
owc-open-preferences riapre il pannello. «Rifiuta tutto» è reso con la stessa prominenza di
«Accetta tutto». La X di chiusura è disattivata di default; se attivata, registra un rifiuto
completo, mai una chiusura silenziosa. Il pannello è utilizzabile da tastiera, i suoi
interruttori portano uno stato visibile, e la modale intrappola e poi restituisce il focus. I
testi sono traducibili e sostituibili tramite il filtro owc_banner_texts.
Che cosa blocca esattamente il blocco automatico? Gli script di terze parti, gli snippet di tracciamento inline, le iframe, i pixel di misurazione, i fogli di stile analytics e marketing, e le sorgenti di media di terze parti vengono riscritti prima del consenso e liberati dopo, nell'ordine del documento. I resource hint verso un terzo catalogato vengono rimossi invece che rinviati, dato che un hint apre una connessione e non ha nulla da ripristinare. I fogli di stile e i font serviti da un host classificato functional vengono lasciati intatti. Gli attributi di lazy loading dei plugin di cache vengono neutralizzati perché un loader non ripristini una URL bloccata. Il plugin include 175 firme, modificabili dalla schermata Tracker catalogue ed estendibili tramite filtro.
Che cosa viene iscritto nel registro? Ogni azione di consenso — accettazione, rifiuto, salvataggio parziale, opt-out GPC, rinnovo, revoca — viene aggiunta alla tabella concatenata. Una riga contiene un IP pseudonimizzato, un'impronta dello user-agent, l'URL di pagina, il profilo di conformità, un'impronta dei documenti in vigore, un'impronta del banner realmente mostrato e la versione del plugin — gli elementi che permettono di ricostruire ciò che il visitatore ha visto.
Quali protezioni ci sono sul modulo dei diritti? Una trappola per bot, un tempo minimo di compilazione, un'attestazione obbligatoria verificata lato server, tre limiti di frequenza distinti, un token monouso memorizzato solo in forma di impronta, e una conferma che richiede un'azione esplicita sulla pagina — così che uno scanner di link di posta non possa confermare un'identità al posto della persona. Una richiesta il cui autore non ha mai confermato la propria identità non può essere esportata, né cancellata, né registrata come evasa, e questo controllo è applicato lato server, non solo nascondendo dei pulsanti.
Quali segnali Google Consent Mode v2 vengono emessi?
Tutti e sette: ad_storage, ad_user_data, ad_personalization, analytics_storage,
functionality_storage, personalization_storage e security_storage. Le sei categorie vi
sono associate a partire da una fonte di verità unica, così che il banner, il blocco e il
bootstrap non possano divergere.
Come vengono gestiti l'opt-out CCPA e GPC? Un controllo «Do Not Sell or Share» viene iniettato nel footer o collocato tramite shortcode. Il primo clic registra l'opt-out, come esigono i regolamenti californiani, invece di aprire un pannello. Il segnale Global Privacy Control è trattato come un opt-out vincolante sotto i profili statunitensi, una volta per sessione di navigazione, e il visitatore viene avvisato se ha sostituito una scelta che aveva registrato. Sotto i profili della famiglia GDPR, GPC è trattato come un indizio: le categorie facoltative sono pre-rifiutate nell'interfaccia, il banner resta visibile, e nulla viene registrato — perché lì il consenso deve essere un atto positivo.
Quali cookie deposita il plugin stesso?
Sono tutti cookie interni, nessuno serve al tracciamento, e sono dichiarati nella cookie
policy generata: owc_consent (le scelte, il token pseudonimo del browser e il profilo;
durata secondo l'impostazione di rinnovo, 12 mesi di default, limitata a 13), owc_geo
(paese e profilo rilevati, 24 ore, senza alcun indirizzo IP), owc_gpc (marcatore di
sessione), owc_gpc_notice (5 minuti), e euconsent-v2 (stringa TCF, solo se il modulo è
attivo e dopo un'azione del visitatore).
Funziona in multisito? Sì. Ogni sito della rete ha le proprie tabelle e le proprie impostazioni. Un'attivazione di rete percorre tutti i siti solo se la rete ne conta al massimo 200; oltre, ogni sito viene predisposto alla sua prima richiesta.
E se elimino il plugin? Il registro, i rilevamenti dello scanner, il catalogo, i collegamenti OW Forms, le opzioni e le impostazioni sopravvivono di default. Due cose spariscono sempre, qualunque sia l'impostazione: le sei attività pianificate, e la tabella delle richieste di diritti — è l'unica che contiene dati personali direttamente identificativi relativi a terzi, e una volta rimosso il plugin nulla ne limita più la conservazione e nessuna schermata permette di rispondervi. Esportare le richieste prima di eliminare il plugin. Per una pulizia completa del resto, attivare «Delete all data on uninstall» prima dell'eliminazione.
Dove trovare il supporto?
- Portale di supporto: https://optionweb.dev/it/addons/support/
- E-mail: support@optionweb.dev
- Documentazione: https://optionweb.dev/it/addons/ow-consent/