Live · stato OK
Documentazione · OW Consent v1.4.3

OW Consent
Documentazione

Il consenso WordPress che blocca i traccianti prima del clic, non dopo.

v1.4.3GPL-2.0-or-laterDocumentazione

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

  1. Panoramica
  2. Installazione
  3. Avvio rapido
  4. Gli undici profili di conformità
  5. Le categorie di cookie
  6. Il banner
  7. Il blocco automatico
  8. Lo scanner dei traccianti
  9. Il generatore di documenti legali
  10. Il registro dei consensi
  11. Il portale dei diritti (DSAR)
  12. L'opt-out CCPA «Do Not Sell or Share»
  13. Google Consent Mode v2
  14. IAB TCF v2.2
  15. Global Privacy Control
  16. Il rilevamento regionale
  17. L'integrazione OW Forms
  18. Il pulsante flottante
  19. Shortcode
  20. REST API
  21. Riferimento delle impostazioni
  22. Risoluzione dei problemi
  23. 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-Cookie depositano; 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 riceve quebec.
  • 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

  1. Scaricare ow-consent-1.4.3.zip da https://optionweb.dev/it/addons/ow-consent/
  2. Plugin → Aggiungi nuovo → Carica plugin
  3. 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 %i di wpdb::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'intestazione Requires at least impedisce 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:

TabellaContenuto
{prefix}owc_ledgerIl registro dei consensi, concatenato tramite hash
{prefix}owc_dsarLe richieste di esercizio dei diritti
{prefix}owc_scannerI rilevamenti dello scanner dei traccianti
{prefix}owc_scriptsIl 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 in INSERT 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_upgrade cinque 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:

  1. Sempre, qualunque sia l'impostazione: i sei cron vengono cancellati e la tabella {prefix}owc_dsar viene 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.
  2. Solo se delete_data_on_uninstall è esplicitamente attivo: eliminazione delle tabelle owc_ledger, owc_dsar, owc_scanner, owc_scripts e owc_form_links, delle opzioni nominate, di tutte le opzioni con prefisso owc_ (transient compresi) e delle usermeta con prefisso owc_. 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

ProfiloRegime copertoModello
gdprGDPR + ePrivacy (UE/SEE)Opt-in
uk_pecrUK GDPR + PECR (Regno Unito)Opt-in
ch_nfadpnLPD svizzeraOpt-in
quebecLegge 25 (Québec)Opt-in
lgpdLGPD (Brasile)Opt-in
popiaPOPIA (Sudafrica)Opt-in
piplPIPL (Cina)Opt-in
dpdpDPDP Act 2023 (India)Opt-in
ccpaCCPA / CPRA (California)Opt-out
us_genericLeggi statali statunitensi genericheOpt-out
auPrivacy 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 cambiaDettaglio
Modello di consensoOpt-in, salvo ccpa, us_generic, au
Valori predefiniti di Consent ModeI sette segnali passano a granted sotto un profilo opt-out
Link «Do Not Sell or Share» obbligatorioccpa e us_generic soltanto
GPC giuridicamente vincolanteccpa e us_generic soltanto — l'Australia è esplicitamente esclusa: regime opt-out, ma che non riconosce GPC
Perimetro «il GDPR si applica» per TCF27 paesi UE + IS, LI, NO + GB + CH, cioè 31 codici
Lingua del documento generatoquebec → francese; lgpd → portoghese; gdpr con paese FR, BE o LU → francese; tutto il resto → inglese
Autorità di controllo citataTabella per profilo, affinata per paese nel caso del GDPR
Elenco dei diritti pubblicatoUn elenco redatto per profilo, con citazione dell'articolo
Corpo «regime cookie» del documento ingleseTesto, 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

ProfiloAutorità nominata
gdpr, paese FRCNIL
gdpr, paese BEAPD-GBA
gdpr, paese LUCNPD
gdpr, paese DEBfDI
gdpr, altro paese SEEFormulazione generica («l'autorità di controllo competente»)
uk_pecrICO
ch_nfadpPFPDT / FDPIC
quebecCommission d'accès à l'information
lgpdANPD
ccpaCalifornia Privacy Protection Agency
popiaInformation Regulator (South Africa)
piplCyberspace Administration of China
dpdpData Protection Board of India
auOAIC
us_genericAttorney 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_generic esiste 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:

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

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.

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

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

functionalfunctional, analyticsmeasurement, marketingadvertising, preferencespersonalization, socialsocial_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

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

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

ImpostazionePredefinitoEffetto
banner_accept_alltrueMostra «Accetta tutto»
banner_reject_alltrueMostra «Rifiuta tutto»
banner_preferencestrueMostra «Personalizza»
banner_close_xfalseMostra la X di chiusura
banner_show_logotrueMostra 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:

  1. Quando banner_close_x è off, la X è presente nel markup ma porta l'attributo hidden, e il CSS la rimuove del tutto: né visibile, né raggiungibile col focus, né annunciata agli screen reader.
  2. Quando è attiva, fare clic sulla X esegue il percorso «Rifiuta tutto» completo, mai una chiusura silenziosa.
  3. 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 un wp_kses limitato 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"> con aria-checked, aria-labelledby e aria-describedby. Lo stato è portato da aria-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 è sticky in 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

ImpostazionePredefinitoLimiti
consent_renewal_months12da 0 a 13
consent_policy_hash_checktrue

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.

  1. 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 il Set-Cookie mentre WordPress risponde 200. Il runtime riancora quindi il percorso fornito da PHP sull'origine realmente visitata.
  2. Nonce. Recuperato fresco su GET /owc/v1/nonce subito prima della scrittura, mai incorporato in HTML memorizzabile in cache, inviato nell'header X-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.
  3. page_url esplicito nel corpo della richiesta, perché la riga di registro non dipenda dall'header Referer, che un'estensione, un meta-referrer o un proxy possono rimuovere.
  4. Protezione anti-blocco di 15 secondi con AbortController: la richiesta viene annullata, non solo ignorata, perché un POST tardivo non scriva una seconda riga.
  5. 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.
  6. 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.
  7. Un solo reinvio, e solo su un 403 il cui codice è owc_bad_nonce, rest_cookie_invalid_nonce o rest_nonce_invalid, e solo se il nonce ottenuto è realmente diverso.
  8. 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 su hashchange;
  • qualsiasi elemento con la classe owc-open-preferences o l'attributo data-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() e wp_footer(): un tema che non li chiama non ottiene nulla.
  • Senza window.fetch e window.Promise, nessuna scrittura è possibile e viene mostrato l'errore browser.
  • 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 da wp_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 stesso Content-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:

  1. già trattato → intatto;
  2. script del plugin stesso → intatto;
  3. type="text/plain" o type non-JS (ld+json, importmap, x-template) → intatto;
  4. 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;
  5. host in allowlist → intatto; risorsa first-party che non sembra un tracciante → intatto;
  6. corrispondenza al catalogo sulla forma host + percorso; una URL data: o javascript: viene decodificata e valutata come un corpo inline;
  7. una regola classificata necessary non viene mai bloccata (Stripe.js, reCAPTCHA, Turnstile, cdnjs…): bloccarla non produce alcuna conformità e rompe lo snippet chiamante;
  8. nessuna corrispondenza → blocker_unknown_script_policy, predefinito allow;
  9. altrimenti riscrittura in type="text/plain" con data-owc-cat, data-owc-vendor, data-owc-src e, se il type originale era particolare (module, per esempio), data-owc-type per 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 » che sblocca la categoria corrispondente.

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é analyticsmarketing — 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 analytics o marketing viene neutralizzato.
  • Google Fonts e gli altri host di tipografia classificati functional non 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_consent viene 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.src e setAttribute vengono avvolti. Un loader first-party che assegna s.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 onload o onerror, con un ritardo massimo di 5 secondi perché un fornitore irraggiungibile non blocchi il resto. L'attributo async viene applicato solo se era presente in origine — senza questo, uno script creato con createElement sarebbe forzato in asincrono e lo snippet di configurazione verrebbe eseguito prima della sua libreria. Per uno script inline ripristinato, document.write viene 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 MutationObserver riesamina 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:

RipartizioneDettaglio
Per bersaglio110 pattern di URL, 65 nomi di cookie
Per tipo172 frammenti di testo, 3 espressioni regolari
Per categorianecessary 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 host che 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

ImpostazionePredefinitoValori
compliance_stricttruebooleano
blocker_unknown_script_policyallowallow, block
blocker_unknown_iframe_policyblockallow, block
blocker_block_resource_hintstruebooleano
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

  1. Senza compliance_strict, vengono trattati solo gli script messi in coda da WordPress.
  2. Uno script di terze parti sconosciuto è autorizzato di default. Il blocco reale poggia sul catalogo e sulle firme inline — vanno arricchiti.
  3. La protezione degli script iniettati da JavaScript copre solo venti host; un tracciante fuori elenco iniettato da codice first-party passa.
  4. I fogli di stile classificati functional (font web) sono volutamente lasciati passare.
  5. Un tracciante catalogato in <img> ma classificato diversamente da analytics o marketing non viene neutralizzato.
  6. I media (object, embed, source, video, audio) non sono mai bloccati di default: viene trattato solo un terzo già catalogato.
  7. 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.
  8. 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

VincoloValore
URL per esecuzionescanner_max_urls, predefinito 25, limiti da 1 a 500
Timeout per richiestascanner_timeout, predefinito 8 s, limiti da 1 a 60
Budget di tempo reale per lottomax_execution_time − 10 s, altrimenti 45 s, limitato tra 5 e 60 s
Lock di esecuzione15 minuti
Durata di vita della coda6 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 cookie diventa una regola match_target = cookie sul nome del cookie;
  • un rilevamento di tipo inline_script scrive 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 host sul 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:

  1. Traccianti non categorizzati — inviata solo per identificatori realmente nuovi, con una memoria limitata a 500 voci.
  2. «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

ImpostazionePredefinitoLimiti
scanner_enabledfalsebooleano
scanner_frequencyweeklyhourly, twicedaily, daily, weekly
scanner_max_urls25da 1 a 500
scanner_timeout8da 1 a 60
scanner_probe_modeadminsadmins, off
scanner_alert_email''indirizzo e-mail

Da sapere: scanner_probe_mode non è consumato in questa versione. Il codice lo segnala esplicitamente — la condizione di stampa della sonda non legge più questa impostazione e si basa unicamente su scanner_enabled. Salvare off memorizza il valore senza disattivare la sonda. Per fermare davvero la sonda, disattivare lo scanner.

Limiti dello scanner

  1. Nessun JavaScript viene eseguito dalla scansione lato server. Senza la sonda, ciò che un tag manager inietta è assente dall'inventario.
  2. La sonda è riservata agli amministratori connessi e non esiste alcuna modalità che la esegua per un visitatore ordinario: richiederebbe una propria informativa preventiva.
  3. Un cookie posato tramite header Set-Cookie viene rilevato ma non è bloccabile.
  4. La scansione non esce mai dal dominio ed è limitata a 25 URL di default: un sito grande non è mai coperto integralmente.
  5. Su un host in cui il loopback HTTP è bloccato (autenticazione HTTP di staging, firewall), la scansione non legge nulla.
  6. Un rilevamento non rivisto da 90 giorni sparisce, e sparisce quindi anche dalla cookie policy.
  7. Lo scanner dipende da WP-Cron: su un sito con DISABLE_WP_CRON e senza cron di sistema, la scansione pianificata non parte.

Il generatore di documenti legali

I quattro documenti

TipoContenutoLingue disponibili
cookie_policyCookie policyfrancese, inglese, portoghese
privacy_policyInformativa privacyfrancese, inglese, portoghese
legal_noticeNote legalisolo francese
termsCondizioni generalisolo 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.

DocumentoCampi richiesti
Tuttilegal_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

ProfiloLingua del documento
quebecfrancese
lgpdportoghese
gdpr con legal_country ∈ {FR, BE, LU}francese
Tutto il restoinglese

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.

  1. 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.
  2. 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.
  3. I traccianti non classificati non vengono nascosti: hanno una sezione propria. Sono quelli che nessuno ha esaminato.
  4. 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.
  5. 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 realiledger_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

MetodoPercorsoParametriAccesso
POST/owc/v1/policies/generatetype (obbligatorio), publish (booleano, predefinito false)manage_options
GET/owc/v1/policies/previewtype (obbligatorio)manage_options

L'HTML dell'anteprima passa da wp_kses_post() prima di essere restituito.

Limiti del generatore

  1. Sono modelli, non consulenza legale. Vanno fatti rileggere.
  2. 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.
  3. Tre sole lingue: francese, inglese, portoghese.
  4. Quattro paesi del SEE hanno un'autorità nominata; altrove, una formulazione generica.
  5. La tabella dei traccianti vale quanto vale la scansione — e il documento lo dice.
  6. 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:

ColonnaTipoContenuto
idbigintChiave primaria
created_atdatetimeMarca temporale UTC
visitor_tokenchar(32)Token pseudonimo del browser, 32 caratteri esadecimali
eventvarchar(20)accept_all, reject_all, save_preferences, withdraw, auto, gpc_opt_out
categoriesvarchar(255)Elenco delle categorie concesse
profilevarchar(20)Profilo di conformità in vigore al momento dell'azione
sourcevarchar(60)banner, preferences, footer_link, api, auto
ip_pseudonymousvarchar(45)IP troncato
ua_hashchar(64)Impronta con salt dello user-agent
page_urlvarchar(500)Pagina in cui l'azione è avvenuta
prev_hashvarchar(128)Impronta della riga precedente
row_hashvarchar(128)Impronta di questa riga
policies_hashchar(64)Impronta dei documenti in vigore
banner_revisionvarchar(40)Impronta del banner realmente mostrato
plugin_versionvarchar(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:

ProvenienzaCondizioneVerdetto
constantOWC_LEDGER_KEY è definitaA prova di manomissione
wp-configAUTH_KEY e AUTH_SALT sono definite, non vuote, diverse tra loro e senza la frase predefinitaA prova di manomissione
databaseAltrimentiNon 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; il Referer viene 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

  1. L'inalterabilità è condizionata, e il plugin lo dice da sé.
  2. 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.
  3. La conservazione è limitata a 20.000 righe per esecuzione quotidiana.
  4. 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:

AttributoPredefinitoRuolo
typesaccess,rectification,erasure,portability,restrict,object,optoutTipi 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: access e portability → esportazione; erasure → cancellazione; gli altri tipi non aprono una richiesta nativa.
  • Il titolare viene notificato a dsar_email e dsar_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.

AzioneComportamento
Scaricare i dati (JSON)Rifiuto 409 se l'identità non è mai stata confermata. Il file è chiamato dsar-<id>-<AAAAMMGG>.json
Cancellare i datiOfferto 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 mesiProroga 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
ChiudereStato resolved o rejected, nota obbligatoria. Impossibile riscrivere una richiesta già chiusa, e impossibile registrare «evasa» su un'identità mai confermata
Rinviare il linkEmette 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:
    1. gli invii mai confermati vengono eliminati — nessun valore probatorio, solo dati personali;
    2. 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;
    3. una richiesta ancora aperta viene conservata, con un messaggio che spiega che deve ricevere risposta prima di essere eliminata;
    4. 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

ImpostazionePredefinitoLimiti
dsar_enabledtruebooleano
dsar_email''contatto pubblico e destinatario delle notifiche
dsar_notify_email''destinatario aggiuntivo
dsar_response_days30da 1 a 30 — mai più di un mese
dsar_token_ttl_days7da 1 a 90
retention_dsar_days1095 (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

  1. 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.
  2. Un invio e-mail difettoso blocca il ciclo: la risposta espone mail_sent: false e il modulo mostra un messaggio che rimanda all'indirizzo di contatto, ma la richiesta resta pending e il termine legale non decorre.
  3. I solleciti di scadenza e la purga dipendono da WP-Cron.
  4. 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"]
AttributoPredefinito
label«Do Not Sell or Share My Personal Information»
classowc-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 fetchPromise (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

  1. Il controllo con un clic richiede fetch e Promise.
  2. Il link è obbligatorio solo sotto i profili ccpa e us_generic; sotto qualsiasi altro profilo non viene né mostrato né rivelato.
  3. 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.

SegnaleRegime opt-inRegime opt-out
ad_storagedeniedgranted
analytics_storagedeniedgranted
ad_user_datadeniedgranted
ad_personalizationdeniedgranted
personalization_storagedeniedgranted
functionality_storagedenied se il sito espone la categoria functional, altrimenti grantedgranted
security_storagegranted sempregranted

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

ImpostazionePredefinitoEffetto
gcm_ads_data_redactiontrueOscura gli identificatori pubblicitari finché ad_storage è negato
gcm_url_passthroughtrueFa 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

ImpostazionePredefinitoLimiti
tcf_enabledfalsebooleano
tcf_cmp_id0da 0 a 4095
tcf_publisher_countryFRcodice 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

  1. Nessun CMP ID (tcf_cmp_id < 1): niente __tcfapi, niente stringa TC, niente cookie euconsent-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.
  2. 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à.
  3. 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 un ETag e un Cache-Control: public, max-age=86400, e gestisce le richieste condizionali. Se non c'è nulla in cache, risponde 503 con un Retry-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

CategoriaFinalità
necessarynessuna — fuori dal perimetro TCF, il che garantisce che «Rifiuta tutto» non possa produrre un consenso per la finalità 1
functional1
analytics1, 8, 9, 10
marketing1, 2, 3, 4, 7
preferences1, 5, 6, 11
social1

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

  1. 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.
  2. Solo segmento Core: nessun segmento disclosedVendors, allowedVendorspublisherTC.
  3. Nessuna publisher restriction viene emessa: il contatore corrispondente vale sempre 0 e l'oggetto delle restrizioni è vuoto.
  4. purposeOneTreatment e useNonStandardTexts valgono sempre false, isServiceSpecific vale sempre true — non c'è portata globale — e la schermata di consenso vale sempre 0.
  5. getVendorList ignora il parametro di versione e restituisce sempre l'elenco in cache.
  6. I consensi dell'editore ricopiano le finalità generali: nessuna finalità personalizzata.
  7. Due sole funzionalità speciali.
  8. 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:

  1. gpc_honor è attivo;
  2. il segnale è presente — l'header Sec-GPC, con ripiego su X-Sec-GPC perché alcuni proxy e CDN rinominano l'header; conta solo il valore esatto 1;
  3. il profilo effettivo è ccpa o us_genericaltrimenti non viene scritto nulla;
  4. 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;
  5. gli header non sono ancora stati inviati — un cookie che non si può posare è una decisione che si riregistrerebbe a ogni richiesta;
  6. il marcatore di sessione owc_gpc è assente: una volta per sessione di navigazione;
  7. 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);
  8. la scelta già memorizzata non soddisfa già il segnale — altrimenti si marca soltanto il browser;
  9. il limite di frequenza condiviso è rispettato: 30 all'ora per IP;
  10. 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_personalization e personalization_storage a denied;
  • tutte le categorie facoltative vengono messe a false solo in memoria;
  • given resta 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

CookieDurataRuolo
owc_gpcsessioneSegna che il segnale è già stato trattato per questo browser
owc_gpc_notice300 sAttiva 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.

ImpostazionePredefinito
geo_enabledfalse
geo_default_profilegdpr
geo_mmdb_enabledfalse

La cascata di rilevamento

Vince il primo che riesce:

  1. Cloudflare (CF-IPCountry) — solo se il sito ha dichiarato di stare dietro Cloudflare;
  2. AWS CloudFront (CloudFront-Viewer-Country) — stessa condizione;
  3. 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;
  4. Database MaxMind locale, se geo_mmdb_enabled è attivo;
  5. 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, Secure in 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

PaeseProfilo
I 30 paesi del SEEgdpr
GB, JE, GG, IMuk_pecr
CHch_nfadp
BRlgpd
ZApopia
CN, HKpipl
INdpdp
AU, NZau
CAquebec
USccpa
Tutto il restoIl 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_country e represented_country sono 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

  1. Disattivato di default, e la variante MaxMind richiede un file da fornire.
  2. Gli header di paese sono ignorati finché non si è dichiarato il proprio proxy.
  3. Nessuna granularità per singolo Stato negli Stati Uniti, né per provincia in Canada.
  4. Il regime del visitatore dipende da JavaScript e dal cookie di 24 ore.
  5. Il profilo us_generic non 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

  1. 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.
  2. Impossibile collegare un invio se OW Forms è configurato per non memorizzare gli invii: non c'è alcuna riga a cui agganciarsi.
  3. Nessun recupero retroattivo: il collegamento inizia dal primo invio successivo all'aggiornamento.
  4. 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.
  5. L'integrazione non modifica mai il plugin OW Forms, le sue tabelle né le sue opzioni.
  6. 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

ImpostazioneValoriPredefinito
floating_button_enabledbooleanotrue
floating_button_positionbottom-left, bottom-right, top-left, top-rightbottom-left
floating_button_stylepill, iconpill
floating_button_labeltesto liberoCookies

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"]
AttributoPredefinito
typesaccess,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"]
AttributoPredefinito
label«Do Not Sell or Share My Personal Information»
classowc-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.

MetodoPercorsoParametriRuolo
GET/nonceaction (predefinito wp_rest)Restituisce un token fresco. Risposta: {nonce, action, header: "X-OWC-Nonce", ttl: 43200}, con no-store e Vary: Cookie
POST/consentevent (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/stateLo 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/gvlServe 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/dsartype (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_out
  • source: banner, preferences, footer_link, api, auto
  • type (DSAR): access, rectification, erasure, portability, restrict, object, optout, withdraw
  • categories: un oggetto, massimo 32 voci, solo valori scalari
  • action (nonce): wp_rest soltanto

Rotte di amministrazione

Tutte richiedono la capability manage_options.

MetodoPercorsoParametriRuolo
GET/ledgerpage (≥1, predefinito 1), per_page (1–200, predefinito 50), visitor_token (32 esadecimali), from, toLegge il registro. I filtri realmente applicati sono restituiti nella risposta
GET/ledger/verifyVerifica la catena da un capo all'altro
GET/settingsLe impostazioni effettive
POST/settingscorpo JSON liberoScrive le impostazioni. Risposta {ok, updated, rejected, settings}
POST/scanner/runAvvia una scansione
POST/scanner/ingestcorpo JSON {page, findings[]}Ricezione delle osservazioni della sonda
GET/scanner/findingspage, per_page (1–200, predefinito 50), filter (slug di categoria o unknown)Elenca i rilevamenti
POST/policies/generatetype (obbligatorio), publish (booleano, predefinito false)Genera un documento
GET/policies/previewtype (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.

  1. Controllo di origine stesso-host. Gratuito, deterministico, non consuma alcun budget.
  2. Token proprio del plugin nell'header X-OWC-Nonce — è una prova, mai un veto. La scelta di non usare X-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.
  3. Fail closed: né origine, né token verificato danno un 403 owc_missing_origin.
  4. 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

CompartimentoBudgetFinestraChiave
nonce1205 minutiindirizzo IP
consent305 minutiindirizzo IP
dsar31 oraindirizzo IP
dsar_email324 oreindirizzo e-mail indicato
dsar_global301 oraintero sito
gpc301 oraindirizzo IP
tcf_gvl101 oraindirizzo 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 ) — restituire 0 disattiva 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, con cookie_set: false e un descrittore cookie (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 un Referer validato allo stesso modo, altrimenti la stringa vuota. La chiave è sempre presente, perché il registro non vada a cercare il Referer grezzo.
  • 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

CodiceHTTPSignificato
owc_bad_param400Parametro non valido
owc_bad_categories400Oggetto categories malformato, troppo grande o non scalare
owc_bad_email400Indirizzo e-mail non valido
owc_bad_dsar400Invio DSAR malformato (trappola per bot inclusa)
owc_dsar_not_attested400Attestazione dell'art. 12.6 assente
owc_no_settings400Nessuna chiave di impostazione nota nel corpo
owc_missing_origin403Né origine né token: fail closed
owc_bad_origin403L'origine dichiarata non è un host di questo sito — la risposta elenca fino a dieci host accettati
owc_bad_nonce403Nonce non valido su /scanner/ingest
owc_jsonp_forbidden403Forma JSONP rifiutata su /nonce
owc_forbidden401/403Capability insufficiente
owc_dsar_disabled404Il portale dei diritti è disattivato lato server
owc_no_template404Nessun template per questo tipo e questo profilo
owc_rate_limited429Limite di frequenza raggiunto
owc_consent_failed500Errore nell'applicazione del consenso
owc_dsar_store_failed500Errore di scrittura della richiesta
owc_gen_failedvariabileGenerazione di documento rifiutata (campi mancanti, barriera di lingua)
owc_ledger_write_refused503Il 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

  1. /consent e /dsar accettano scritture anonime per progettazione; la protezione è l'origine più il limite di frequenza, non un nonce.
  2. Il limitatore di frequenza fallisce in apertura senza object cache.
  3. 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.
  4. /scanner/ingest accetta solo manage_options: la sonda non può provenire da un visitatore.
  5. /settings non ha un nonce proprio: sono manage_options più 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.

ImpostazioneValori accettati
banner_positionbottom-bar, bottom-card, center-modal, top-bar
banner_styleauto, light, dark
floating_button_positionbottom-left, bottom-right, top-left, top-right
floating_button_styleicon, pill
compliance_profile, geo_default_profilegdpr, ccpa, lgpd, popia, pipl, dpdp, quebec, uk_pecr, ch_nfadp, au, us_generic
ledger_hash_algosha256, sha3-256 (intersecati con ciò che PHP supporta)
scanner_frequencyhourly, twicedaily, daily, weekly
scanner_probe_modeadmins, off
legal_business_typeauto, vitrine, rental, ecommerce, services, saas, content
blocker_unknown_script_policy, blocker_unknown_iframe_policyallow, block

Limiti degli interi

ImpostazioneLimiti
consent_renewal_monthsda 0 a 13 (0 è trattato come 13)
dsar_response_daysda 1 a 30
dsar_token_ttl_daysda 1 a 90
ledger_retention_daysda 0 a 3650 (0 = illimitato)
scanner_max_urlsda 1 a 500
scanner_timeoutda 1 a 60
legal_minor_ageda 13 a 18
retention_form_data_daysda 1 a 3650
retention_dsar_daysda 1 a 3650
tcf_cmp_idda 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

SchedaImpostazioni
Bannerbanner_*, text_*, floating_button_*, consent_renewal_months, consent_policy_hash_check
Compliancecompliance_profile, compliance_strict, gcm_*, blocker_*, ccpa_inject_footer, gpc_honor, tcf_*, geo_*, auto_footer_menu_inject, forms_integration_enabled, delete_data_on_uninstall, link_*
Legal identitylegal_*, retention_form_data_days, retention_dsar_days, policy_disclaimer
Categoriescat_*_available, cat_*_label, cat_*_desc
Audit ledgerledger_enabled, ledger_retention_days (l'algoritmo è mostrato, non modificabile)
Scannerscanner_*
DSAR requestsdsar_*

Tabelle

TabellaContenuto
{prefix}owc_ledgerRegistro dei consensi concatenato
{prefix}owc_dsarRichieste di esercizio dei diritti
{prefix}owc_scannerRilevamenti dello scanner
{prefix}owc_scriptsCatalogo di firme del blocco
{prefix}owc_form_linksCollegamenti 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

CostanteEffetto
OWC_LEDGER_KEYChiave HMAC del registro, e chiave derivata del cookie di rilevamento regionale
OWC_TRUSTED_PROXY / OWC_TRUSTED_PROXIESIntervalli CIDR dei proxy attendibili; senza di esse, gli header di IP client sono ignorati
OWC_GEO_TRUSTED_HEADERScloudflare, cloudfront, proxy o all
OWC_BEHIND_CLOUDFLAREScorciatoia per l'header di paese di Cloudflare
OWC_BEHIND_CLOUDFRONTScorciatoia per l'header di paese di CloudFront

Gli hook per sviluppatori

Filtri

FiltroRuolo
owc_settingsImpostazioni effettive. La risposta deve dipendere da qualcosa di stabile per tutta la richiesta
owc_banner_textsTesti del banner
owc_bootstrap_configConfigurazione statica stampata in <head>mai un dato per visitatore
owc_scripts_catalogRighe del catalogo prima della validazione
owc_blocker_allowlistHost mai bloccati
owc_scanner_urlsURL da scansionare (poi nuovamente ristrette all'host del sito)
owc_throttle_maxBudget di un limite di frequenza; 0 lo disattiva
owc_allowed_request_hostsHost accettati per una scrittura pubblica
owc_allow_headerless_writeSolleva il fail closed sulle scritture senza origine (predefinito false)
owc_geo_mmdb_pathPercorso del database MaxMind
owc_tcf_purpose_mapCorrispondenza categorie → finalità TCF
owc_tcf_stub_configConfigurazione statica dello stub TCF
owc_policy_templateCorpo grezzo di un template — disattiva il blocco di lingua
owc_policy_varsVariabili di un documento
owc_policy_htmlHTML finale di un documento
owc_dsar_typesTipi di richiesta proposti e accettati
owc_dsar_form_noticeInformativa sotto il modulo
owc_dsar_export_bundleContenuto del bundle di portabilità
owc_dsar_show_fulfilment_panelVisualizzazione del pannello di esecuzione

Azioni

AzioneFirma
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:

  1. banner_enabled è attivo? Il badge in cima alla schermata di amministrazione lo dice.
  2. Il tema chiama wp_head() e wp_footer()? Senza di essi, il banner rifiuta di stamparsi invece di produrre un markup inerte.
  3. Il visitatore ha già fatto una scelta? Il banner compare solo su <html data-owc="none">. Provare in navigazione privata o con window.OWCBanner.reset().
  4. Il visitatore ha JavaScript? Senza di esso il banner resta nascosto — di proposito — e subentra il blocco <noscript>.
  5. 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.

  1. 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'header Host. Il rimedio è servire il sito su un solo host canonico.
  2. consent_policy_hash_check è attivo e i documenti sono cambiati — è il comportamento voluto, il visitatore viene risollecitato dopo una modifica dell'informativa.
  3. 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 filtro owc_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:

  1. Una CDN o un proxy non dichiarato: tutti i visitatori arrivano con lo stesso indirizzo e condividono lo stesso budget. Dichiararlo con OWC_TRUSTED_PROXY in wp-config.php.
  2. 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.

  1. Verificare che la tabella {prefix}owc_ledger esista. La scheda Audit ledger mostra un messaggio leggibile «non ancora installato» invece di una schermata bianca.
  2. Verificare i permessi di scrittura dell'utente MySQL.
  3. Come ultima risorsa, disattivare temporaneamente ledger_enabled per ripristinare il servizio — sapendo che nel frattempo si perde la prova.

Alcuni traccianti si caricano ancora prima del consenso

  1. compliance_strict è attivo? Senza di esso vengono trattati solo gli script messi in coda da WordPress.
  2. 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.
  3. È iniettato da JavaScript first-party? La protezione dinamica copre solo venti host. Aggiungere una regola di catalogo sul dominio del tracciante.
  4. È un cookie posato tramite header Set-Cookie? Nessun blocco lato client può trattenerlo. Va trattato alla fonte, lato server.
  5. Portare blocker_unknown_script_policy su block — 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

  1. È attivo? È disattivato di default.
  2. WP-Cron funziona? Con DISABLE_WP_CRON e senza cron di sistema, la scansione pianificata non parte mai. Avviarne una a mano per verificare.
  3. Il loopback HTTP è possibile? Un'autenticazione HTTP di staging o un firewall blocca la scansione. Il pannello di copertura mostra il primo errore.
  4. 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.

  1. La risposta dell'API porta mail_sent: false quando l'invio è fallito, e il modulo mostra allora un messaggio dedicato che rimanda all'indirizzo di contatto.
  2. Installare un plugin SMTP. La funzione mail() di PHP viene rifiutata dalla maggior parte dei server di ricezione.
  3. 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.
  4. Verificare che dsar_email sia 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_id vale 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 raggiungere vendor-list.consensu.org in HTTPS uscente.

Tutti i visitatori ricevono il profilo configurato, nonostante il rilevamento regionale

  1. geo_enabled è attivo? È disattivato di default.
  2. È stato dichiarato il proxy? Senza OWC_BEHIND_CLOUDFLARE, OWC_BEHIND_CLOUDFRONT o OWC_GEO_TRUSTED_HEADERS in wp-config.php, gli header di paese sono ignorati, perché sono falsificabili.
  3. Il file MaxMind è al suo posto? Il plugin non lo scarica e non offre una schermata di caricamento.
  4. 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?