En vivo · estado OK
Documentación · OW Consent v1.4.3

OW Consent
Documentación

El consentimiento en WordPress que bloquea los rastreadores antes del clic, no después.

v1.4.3GPL-2.0-or-laterDocumentación

OW Consent — Documentación

La gestión completa del consentimiento en WordPress: banner multijurisdicción, bloqueo de rastreadores, escáner, documentos legales, registro probatorio y portal de derechos. Autor: OptionWeb — Julien Daniel Página del plugin: https://optionweb.dev/es/addons/ow-consent/ Licencia: GPL-2.0-or-later Versión cubierta por este documento: 1.4.3


Índice

  1. Visión general
  2. Instalación
  3. Inicio rápido
  4. Los once perfiles de cumplimiento
  5. Las categorías de cookies
  6. El banner
  7. El bloqueo automático
  8. El escáner de rastreadores
  9. El generador de documentos legales
  10. El registro de consentimiento
  11. El portal de derechos (DSAR)
  12. La exclusión CCPA «Do Not Sell or Share»
  13. Google Consent Mode v2
  14. IAB TCF v2.2
  15. Global Privacy Control
  16. La detección regional
  17. La integración con OW Forms
  18. El botón flotante
  19. Shortcodes
  20. API REST
  21. Referencia de ajustes
  22. Solución de problemas
  23. FAQ

Visión general

OW Consent es una suite de gestión del consentimiento para WordPress. Cubre la cadena entera: mostrar una elección, aplicarla realmente a los rastreadores, conservar la prueba, publicar los documentos que la explican y recibir las solicitudes de los interesados.

Se incluyen once perfiles de cumplimiento — del RGPD a la CCPA, pasando por la Ley 25 de Quebec, la LGPD brasileña o la DPDP india. El perfil activo decide el modelo de consentimiento (opt-in u opt-out), los valores por defecto de Google Consent Mode, el texto de los documentos generados, los derechos publicados y la autoridad de control citada.

Todo se queda en su sitio web. El plugin solo contacta con un servicio externo, la Global Vendor List de IAB Europe, y únicamente si activa el módulo TCF, que viene desactivado por defecto. Sin telemetría, sin cuenta, sin suscripción.

Compatible con una caché de página completa, por construcción

Es el punto de arquitectura que gobierna todo lo demás. Nada de lo que el servidor renderiza depende de la cookie de consentimiento. El HTML es idéntico para todos los visitantes: los rastreadores se reescriben como etiquetas inertes para todo el mundo, y es un runtime JavaScript el que los libera en el navegador, categoría por categoría, leyendo la cookie antes del primer pintado.

Consecuencia práctica: LiteSpeed Cache, WP Rocket, Varnish o un CDN no pueden servir las decisiones de un visitante a otro. Solo las respuestas realmente personales — la página de confirmación de identidad de una solicitud de derechos, las respuestas REST que contienen datos del visitante — se marcan explícitamente como no cacheables. Tampoco se imprime ningún nonce en HTML cacheable: el banner pide uno fresco a un endpoint no-store justo antes de cada escritura.

Lo que se incluye

  • Banner de consentimiento: cuatro posiciones, tema claro / oscuro / automático, «Rechazar todo» tan visible como «Aceptar todo», panel de preferencias accesible por teclado, totalmente traducible.
  • Bloqueo automático de rastreadores: scripts, snippets en línea, iframes, píxeles, pistas de recursos (preconnect, dns-prefetch, preload), hojas de estilo de analítica/marketing y medios de terceros, a partir de 175 firmas incluidas y editables.
  • Escáner de rastreadores: barrido de sus propias páginas mediante WP-Cron, lectura de las cabeceras Set-Cookie, sonda de navegador reservada a los administradores, y un panel que siempre dice lo que el barrido ha cubierto realmente.
  • Generador de documentos legales: política de privacidad y política de cookies para los once perfiles, en el idioma de la jurisdicción; aviso legal y condiciones generales en francés.
  • Registro de consentimiento: cada acción se añade a una tabla encadenada por HMAC, con el perfil vigente, la huella de los documentos publicados y la huella del banner realmente mostrado.
  • Portal de derechos (DSAR): formulario por shortcode, verificación por email, plazo del artículo 12.3 disparado en la confirmación de identidad, conexión con el exportador y el eliminador nativos de WordPress.
  • Google Consent Mode v2, IAB TCF v2.2, Global Privacy Control, detección regional, integración con OW Forms: cada uno activable de forma independiente.
  • API REST completa bajo el namespace owc/v1.

Lo que no hace — léalo antes de comprometerse

En un asunto jurídico, una promesa excesiva le expone a usted tanto como al editor. Estos son los límites, tal y como están escritos en el código.

  • Los documentos generados son plantillas, no asesoramiento jurídico. Cada documento termina con una advertencia que lo dice, y esa advertencia está activada por defecto. Haga revisar sus documentos antes de publicarlos.
  • El módulo TCF no es una CMP registrada ante IAB Europe. Exige un CMP ID que debe obtener usted mismo, no ofrece ninguna elección a nivel de proveedor, y los proveedores están en su derecho de rechazar su señal. Si los ingresos publicitarios bajo TCF le importan, use una CMP certificada.
  • El registro solo es a prueba de manipulación bajo condición. La cadena únicamente es una prueba si su clave de firma vive fuera de la base de datos. El plugin detecta el caso contrario, lo informa él mismo y se lo dice en la administración en lugar de pretender lo opuesto.
  • El escáner no ejecuta JavaScript. Ve lo que contiene su HTML y lo que depositan sus cabeceras Set-Cookie; lo que un gestor de etiquetas inyecta en tiempo de ejecución solo lo ve una sonda reservada a los administradores conectados.
  • El bloqueo por defecto no es «todo lo que sea de terceros». Un script de terceros desconocido se permite por defecto; son los iframes desconocidos los que se bloquean por defecto. El bloqueo se apoya en el catálogo de firmas, que usted puede enriquecer.
  • Sin JavaScript no se puede registrar ninguna elección. El visitante ve un bloque <noscript> y no se carga nada que no sea imprescindible, pero tampoco se registra nada.
  • Sin granularidad estado por estado en EE. UU. ni provincia por provincia en Canadá dentro de la detección regional: todo el territorio estadounidense recibe el perfil ccpa, todo Canadá recibe quebec.
  • El aviso legal y las condiciones generales solo existen en francés. Para cualquier jurisdicción cuyo idioma de destino no sea el francés, el generador se niega a producir esos dos documentos en lugar de publicar un texto inadecuado.
  • Sin rol dedicado: toda la administración exige la capacidad manage_options.

Esta lista es una decisión asumida, no una hoja de ruta disimulada. En materia de cumplimiento, una herramienta que enuncia sus agujeros vale más que una que los esconde.


Instalación

Desde el .zip

  1. Descargue ow-consent-1.4.3.zip desde https://optionweb.dev/es/addons/ow-consent/
  2. Plugins → Añadir nuevo → Subir plugin
  3. Elija el archivo, pulse Instalar y después Activar

Por FTP

Descomprima el archivo y deposite la carpeta ow-consent en /wp-content/plugins/, después active el plugin desde Plugins.

Requisitos

  • WordPress 6.2 o más reciente — es un rechazo de arranque, no una recomendación (véase más abajo)
  • PHP 7.4 o más reciente
  • MySQL 5.7+ / MariaDB 10.2+
  • WP-Cron operativo si usa el escáner, la retención del registro, los recordatorios de vencimiento DSAR o el módulo TCF

El cortafuegos de WordPress 6.2. Desde la 1.3.0, cada identificador de tabla se pasa por el marcador %i de wpdb::prepare(), que WordPress solo entiende a partir de la 6.2. En un núcleo más antiguo, prepare() devolvería una cadena vacía, el catálogo de rastreadores se resolvería en silencio a un conjunto vacío, y el banner seguiría prometiendo al visitante un bloqueo que no se produciría. Por eso el plugin se niega a arrancar y muestra un mensaje de error que dice explícitamente que no se bloquea nada y que no se registra ningún consentimiento. La cabecera Requires at least ya impide la activación por debajo de 6.2, pero no cubre ni una degradación del núcleo bajo una instalación en servicio, ni una copia depositada por FTP.

Lo que se instala

Al activarse, OW Consent crea cuatro tablas:

TablaContenido
{prefix}owc_ledgerEl registro de consentimiento, encadenado por hash
{prefix}owc_dsarLas solicitudes de ejercicio de derechos
{prefix}owc_scannerLos hallazgos del escáner de rastreadores
{prefix}owc_scriptsEl catálogo de firmas que usa el bloqueo

Una quinta tabla, {prefix}owc_form_links, la crea por separado la integración con OW Forms si el plugin hermano está activo (véase La integración con OW Forms).

La activación añade además:

  • la opción owc_settings, sembrada vacía — deliberadamente: mientras no haya guardado nada, todos los textos vienen de los valores por defecto en inglés traducidos al vuelo al idioma del sitio, en lugar de congelar en la base de datos la configuración regional de quien activó el plugin;
  • la opción owc_version;
  • el catálogo de rastreadores incluido (data/tracker-catalog.json), insertado en lotes de 100 con INSERT IGNORE, con una marca de siembra por versión (owc_catalog_seeded_1.4.3);
  • la tarea diaria owc_daily_maintenance, programada una hora después de la activación.

Otros cinco eventos de cron los arman los módulos que los utilizan. Los seis hooks de cron del plugin son: owc_scanner_run, owc_scanner_run_batch, owc_ledger_retention, owc_tcf_refresh_gvl, owc_run_upgrade y owc_daily_maintenance.

En multisitio, las tablas se crean por sitio, nunca compartidas (wp_2_owc_ledger, etc.). Una activación de red recorre todos los sitios solo si la red tiene como máximo 200 sitios; por encima de esa cifra, cada sitio se aprovisiona de forma perezosa en su primera petición. Un subsitio creado después de una activación de red se aprovisiona mediante el hook wp_initialize_site.

Qué definir en wp-config.php

Ninguna de estas constantes es obligatoria, pero dos de ellas cambian el valor probatorio de lo que produce el plugin.

// Recomendado: saca la clave de firma del registro fuera de la base de datos.
define( 'OWC_LEDGER_KEY', 'una cadena aleatoria larga, propia de este sitio' );

// Recomendado: los salts estándar de WordPress. Sin ellos, WordPress los guarda en la
// base de datos, y el propio registro se señala como no a prueba de manipulación.
define( 'AUTH_KEY',  '…' );
define( 'AUTH_SALT', '…' );

// Si el sitio está detrás de un CDN, un balanceador de carga o un proxy inverso.
// Sin esta constante, CF-Connecting-IP, X-Forwarded-For y X-Real-IP se IGNORAN
// y solo se usa REMOTE_ADDR — lo que da la misma identidad a todos sus visitantes
// a efectos de limitación de peticiones.
define( 'OWC_TRUSTED_PROXY', '198.51.100.0/24, 2001:db8::/32' );
// `OWC_TRUSTED_PROXIES` se acepta como alias; sirve una cadena o un array.

// Solo para la detección regional: declara qué cabeceras de país son de confianza.
define( 'OWC_GEO_TRUSTED_HEADERS', 'cloudflare' ); // 'cloudflare'|'cloudfront'|'proxy'|'all'
define( 'OWC_BEHIND_CLOUDFLARE', true );
define( 'OWC_BEHIND_CLOUDFRONT', true );

OWC_LEDGER_KEY sirve también para derivar la clave de la cookie de detección regional. Si no está definida, la clave del registro cae de nuevo en wp_salt('auth').

Quién tiene acceso

Toda la administración del plugin y todas las rutas REST de administración exigen la capacidad manage_options. No existe ni un rol dedicado ni una capacidad más fina: abrir la pantalla del plugin a alguien equivale a darle los ajustes del sitio.

Ruta de actualización

El plugin nunca migra su esquema en caliente en una página anónima. Cuando la versión cambia, owc_version y una marca owc_pending_upgrade se escriben de inmediato, y después:

  • la migración se ejecuta en línea si la petición es una petición de administración fuera de AJAX, una ejecución de cron o un comando WP-CLI;
  • si no, se programa un evento owc_run_upgrade cinco segundos más tarde.

admin-ajax.php se trata como una petición anónima: es un punto de entrada público. La migración se ejecuta bajo bloqueo (owc_upgrade_lock, robado al cabo de 300 segundos), y una red de seguridad en admin_init recoge los sitios donde WP-Cron está desactivado. En una versión idéntica, el coste total es un único get_option().

Desactivación y eliminación

Desactivar conserva todos los datos y solo borra los seis eventos de cron. En desactivación de red, se recorren todos los sitios en lotes de 200 — al contrario que en la activación — porque un cron dejado armado no desaparecería nunca.

Eliminar el plugin dispara uninstall.php, que procede en dos tiempos:

  1. Siempre, sea cual sea su ajuste: se borran los seis crons y se elimina la tabla {prefix}owc_dsar, con sus transients de limitación de peticiones. Es la única tabla que contiene datos personales directamente identificativos de terceros (dirección de email, nombre, texto libre); una vez el plugin ha desaparecido, ya nada acota su retención y ninguna pantalla permite responderlas, exportarlas ni borrarlas. Exporte las solicitudes que deba conservar antes de eliminar el plugin.
  2. Solo si delete_data_on_uninstall está explícitamente activado: eliminación de las tablas owc_ledger, owc_dsar, owc_scanner, owc_scripts y owc_form_links, de las opciones nombradas, de todas las opciones con el prefijo owc_ (transients incluidos) y de las usermeta con el prefijo owc_. En multisitio, las opciones de red siguen la decisión del sitio principal.

Este ajuste está desactivado por defecto: la prueba de consentimiento, exigida por el artículo 7.1 del RGPD, sobrevive a la eliminación del plugin.


Inicio rápido

Tras la activación, abra la entrada OW Consent en la barra lateral de la administración. Todo el plugin cabe en esa única pantalla, en diez pestañas: Dashboard, Banner, Compliance, Legal identity, Categories, Policies, Scanner, Tracker catalogue, Audit ledger, DSAR requests.

Guardar solo afecta a la pestaña abierta. Es deliberado: cada booleano tiene un campo oculto gemelo, y una clave ausente del formulario significa «este campo está en otra pestaña», nunca «desmarcado». Sin eso, guardar una pestaña machacaría los ajustes de todas las demás.

1. Elija su perfil de cumplimiento

Pestaña Compliance. El perfil decide el modelo de consentimiento, los valores por defecto de Consent Mode, los documentos generados y los derechos publicados. Por defecto: gdpr.

compliance_strict está activado por defecto: es lo que extiende el bloqueo más allá de los scripts registrados por WordPress. Déjelo activo si quiere que se traten los iframes, los píxeles y los scripts escritos a pelo en su tema.

2. Rellene la identidad legal

Pestaña Legal identity. Estos campos son la materia prima de los documentos generados, y la generación se rechaza mientras haya un campo obligatorio vacío — con la lista de las claves que faltan, no un fallo silencioso.

Mínimo para todos los documentos: legal_company_name y legal_company_email. Para todo salvo la política de cookies, añada la dirección y el país. Para el aviso legal, añada además el teléfono, el director de la publicación y los datos completos del proveedor de alojamiento; en Francia, Bélgica y Luxemburgo, la forma jurídica y el número de registro pasan a ser obligatorios también.

Deje legal_dpa_authority vacío: la autoridad de control se deriva de su país y de su perfil. Rellenarla a mano en un sitio multijurisdicción equivale a nombrar al regulador equivocado.

3. Revise sus categorías

Pestaña Categories. Las seis categorías están disponibles por defecto. Desactive las que su sitio no use: una categoría ausente de la interfaz no es una ganancia de cumplimiento, es un recurso bloqueado para siempre sin ningún interruptor que lo libere — el bloqueo lo vuelca entonces sobre marketing.

Deje las etiquetas vacías mientras le sirvan: así siguen el idioma del sitio. En cuanto personaliza un texto, ese texto deja de seguir al idioma.

4. Ajuste el banner

Pestaña Banner. Posición, tema, botones, etiquetas, plazo de renovación.

Dos puntos que no conviene pasar por alto: deje banner_reject_all activado («Rechazar» debe ser tan sencillo y visible como «Aceptar»), y deje la aspa de cierre desactivada — no está por defecto porque cerrar sin elegir equivale a un rechazo implícito. Si la activa, un clic en el aspa ejecuta la ruta «Rechazar todo» completa, nunca un cierre silencioso.

5. Genere sus documentos

Pestaña Policies. Cuatro documentos: política de cookies, política de privacidad, aviso legal, condiciones generales. Cada uno se convierte en una página de WordPress versionada, cuyo enlace se reinyecta en los ajustes.

Genere primero en vista previa, revise, y publique después. Y hágalo revisar por un profesional: la banda de advertencia al pie de cada documento no es decorativa.

6. Abra el portal de derechos

Cree una página y pegue en ella [owc_dsar_form]. Rellene dsar_email en la pestaña DSAR requests: es la dirección de contacto publicada bajo el formulario y el destinatario de las notificaciones. Sin ella, el plugin cae en legal_dpo_email y después en admin_email — pero admin_email nunca se publica en una página pública.

7. Compruébelo antes de abrir al público

El panel de control ejecuta quince comprobaciones de cumplimiento y distingue los errores de las advertencias: banner desactivado, «Rechazar todo» ausente, aspa de cierre activada, identidad legal incompleta, portal de derechos cerrado, registro desactivado, ninguna política enlazada, autoridad de control en contradicción con el perfil activo (con un botón «Fix this» que la reinicia), sitio sin HTTPS, rastreadores sin categorizar, solicitudes DSAR fuera de plazo, reglas de catálogo que no pueden dispararse.

Ponga esas quince líneas en verde antes de anunciar que su sitio cumple.


Los once perfiles de cumplimiento

El perfil de cumplimiento no es cosmético. Pilota el modelo legal, la interfaz, las señales técnicas y el contenido de los documentos publicados.

La lista

PerfilRégimen cubiertoModelo
gdprRGPD + ePrivacy (UE/EEE)Opt-in
uk_pecrUK GDPR + PECR (Reino Unido)Opt-in
ch_nfadpnLPD suizaOpt-in
quebecLey 25 (Quebec)Opt-in
lgpdLGPD (Brasil)Opt-in
popiaPOPIA (Sudáfrica)Opt-in
piplPIPL (China)Opt-in
dpdpDPDP Act 2023 (India)Opt-in
ccpaCCPA / CPRA (California)Opt-out
us_genericLeyes estatales estadounidenses genéricasOpt-out
auPrivacy Act (Australia)Opt-out

Ajuste: compliance_profile, por defecto gdpr.

Opt-in significa que, antes de cualquier elección, solo se conceden las categorías forzadas. Opt-out significa que todo está concedido hasta el rechazo. Los tres perfiles opt-out son ccpa, us_generic y au. El runtime JavaScript aplica exactamente la misma regla que PHP, de modo que servidor y navegador no pueden informar de dos estados distintos.

Lo que el perfil cambia realmente

Lo que cambiaDetalle
Modelo de consentimientoOpt-in, salvo ccpa, us_generic, au
Valores por defecto de Consent ModeLas siete señales pasan a granted bajo un perfil opt-out
Enlace «Do Not Sell or Share» obligatorioccpa y us_generic únicamente
GPC jurídicamente vinculanteccpa y us_generic únicamente — Australia queda excluida explícitamente: régimen opt-out, pero que no reconoce el GPC
Alcance de «el RGPD se aplica» para TCF27 países de la UE + IS, LI, NO + GB + CH, es decir 31 códigos
Idioma del documento generadoquebec → francés; lgpd → portugués; gdpr con país FR, BE o LU → francés; todo lo demás → inglés
Autoridad de control citadaTabla por perfil, afinada por país para el RGPD
Lista de derechos publicadaUna lista redactada por perfil, con cita del artículo
Cuerpo «régimen de cookies» del documento en inglésTexto, alcance de transferencia y garantías distintos por perfil

Los documentos generados por perfil

La política de privacidad y la política de cookies existen para los once perfiles. Las plantillas se resuelven en este orden, ganando la primera encontrada: <tipo>_<perfil>_<idioma>, luego <tipo>_<perfil>, luego <tipo>_<idioma>, luego <tipo>.

Los cuerpos en inglés de la política de cookies son conscientes del perfil: regla aplicable, alcance de transferencia, garantías y etiqueta del panel difieren para gdpr, uk_pecr, ch_nfadp, au, pipl, dpdp, ccpa y us_generic, con las citas correspondientes (art. 5(3) de la directiva 2002/58, PECR reg. 6, art. 45c(b) LTC y art. 19/6(7)(b) nLPD, APP 8, art. 24 PIPL, secciones 5/6/7/9(3)/16 de la DPDP Act 2023, §1798.121 y Cal. Code Regs. tit. 11 §7025, VCDPA/CPA/CTDPA/UCPA/TDPSA).

Autoridad de control citada

PerfilAutoridad nombrada
gdpr, país FRCNIL
gdpr, país BEAPD-GBA
gdpr, país LUCNPD
gdpr, país DEBfDI
gdpr, otro país del EEEFormulación genérica («la autoridad de control 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_genericFiscal General de su estado

Si ha modificado legal_dpa_authority, lo que usted escriba prevalece sobre esta derivación.

Límites de la cobertura jurisdiccional

  • Solo cuatro países del EEE tienen autoridad nombrada (FR, BE, LU, DE). Un sitio RGPD en España, Italia o los Países Bajos publica una formulación genérica.
  • Sin granularidad estado por estado en EE. UU. El perfil us_generic existe y lleva sus propios textos VCDPA/CPA/CTDPA/UCPA/TDPSA, pero ninguna detección automática lo asigna: se elige a mano.
  • Sin granularidad provincial en Canadá.
  • Solo tres idiomas de documentos: francés, inglés, portugués. Los cuerpos no pasan por el mecanismo de traducción de WordPress — es deliberado: un documento legal debe ser monolingüe y su idioma sigue a la jurisdicción, nunca a la configuración regional del administrador.
  • Aviso legal y condiciones generales: plantillas solo en francés. El generador se niega en redondo para cualquier otro idioma de destino, salvo que usted aporte su propio cuerpo mediante el filtro owc_policy_template.

Las categorías de cookies

La lista canónica

Seis categorías, en este orden de visualización, con necessary siempre a la cabeza:

SlugEtiqueta incluidaDescripción incluida
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.

Los valores anteriores son los predeterminados en inglés que se pueden almacenar. En un sitio en español, el visitante ve la traducción — véase el mecanismo de traducción más abajo.

Disponibilidad

Ajuste cat_<slug>_available, uno por categoría. Las seis están disponibles por defecto. La antigua clave cat_<slug>_enabled se sigue leyendo como reserva, sin migración.

preferences y social se entregan disponibles porque el catálogo semilla clasifica YouTube, Spotify, SoundCloud, Instagram, X y Facebook en social: sin la categoría en la interfaz, esas integraciones quedarían bloqueadas para siempre sin manera de optar por ellas.

necessary está forzada: no se puede desactivar (art. 5.3 ePrivacy). Por eso no tiene interruptor de disponibilidad en la administración, solo una etiqueta y una descripción.

El mecanismo de traducción

Ajustes cat_<slug>_label y cat_<slug>_desc. El valor almacenado se devuelve literal únicamente si no está vacío y difiere del valor por defecto en inglés; si no, lo que sale es la traducción.

Consecuencia que conviene conocer: mientras no personalice nada, cambiar el idioma del sitio cambia el idioma de las categorías. En cuanto escribe su propio texto, ese texto queda congelado en el idioma en que lo escribió. La misma regla se aplica a los textos del banner.

La descripción traducida de necessary se construye a partir de dos cadenas: la segunda nombra las cookies del propio plugin, owc_consent (hasta 13 meses) y owc_geo (24 horas, escrita únicamente cuando la detección regional está activada).

Correspondencia con Google Consent Mode v2 — vinculante

Es la fuente de verdad única del plugin: el banner, el bloqueo y el bootstrap leen todos esta tabla, así que no pueden divergir.

CategoríaSeñales de 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

Matiz importante: si su sitio expone la categoría functional, functionality_storage se retira de la lista de necessary. De lo contrario se concedería antes de cualquier consentimiento, cuando pertenece a una categoría opcional.

Correspondencia con TCF — solo descriptiva

functionalfunctional, analyticsmeasurement, marketingadvertising, preferencespersonalization, socialsocial_media.

Son etiquetas de visualización. La correspondencia vinculante del lado TCF es la tabla de identificadores numéricos de finalidades de IAB, descrita en la sección IAB TCF v2.2. No conecte nunca un comportamiento TCF a estas cinco etiquetas.

Qué pasa si una categoría desaparece

Si una regla del catálogo apunta a una categoría que su sitio ya no expone — porque ha desactivado social, por ejemplo — el bloqueo vuelca el recurso sobre marketing, o en su defecto sobre la primera categoría opcional disponible. Sin esa normalización, el recurso quedaría bloqueado para siempre sin ningún interruptor que lo liberase.


El banner

Cuándo se muestra

El banner se renderiza en wp_footer con prioridad 5, y sus recursos se encolan en wp_enqueue_scripts. Sale de inmediato si se cumple alguna de estas condiciones: contexto de administración, feed RSS, robots.txt, o banner_enabled desactivado. Sale también si wp_head nunca se ha disparado — un tema que no llama a wp_head() obtendría en otro caso un marcado inerte y sin estilos.

El HTML es idéntico para todos los visitantes. Todas las casillas del panel se renderizan en OFF desde el servidor y luego se hidratan en el cliente a partir de la cookie. Es el JavaScript el que pone <html data-owc="given|none"> de forma síncrona, antes del primer pintado; el CSS solo muestra el banner con data-owc="none".

Corolario que conviene conocer: un visitante sin JavaScript nunca ve el banner, así que nunca queda encerrado tras una modal que no puede cerrar. En su lugar ve un bloque <noscript> que explica que sus preferencias no se pueden registrar y que no se carga ninguna cookie no imprescindible mientras no se haya tomado una decisión.

Posiciones y tema

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

Solo center-modal obtiene role="dialog", aria-modal="true", un fondo oscurecido, una trampa de foco y la tecla Escape. Las otras tres posiciones son un role="region": Escape no se intercepta (el tema conserva su propia gestión) y el foco no se roba en el primer pintado, lo que empujaría a un usuario de teclado más allá de todos los enlaces de salto.

La paleta oscura se aplica bajo prefers-color-scheme: dark con protecciones para no machacar un tema que declara explícitamente light. El tema del sitio puede pilotar la paleta mediante variables CSS: --owc-paper-tint, --owc-ink, --owc-smoke, --owc-fog, --owc-ui, --owc-accent, --owc-accent-strong, --owc-accent-darker.

Detalles de implementación: z-index 99998 para el banner y 99997 para el fondo, soporte de env(safe-area-inset-*) para los notches de iOS, y desplazamiento automático bajo la barra de administración de WordPress en posición superior.

Los botones

AjustePor defectoEfecto
banner_accept_alltrueMuestra «Aceptar todo»
banner_reject_alltrueMuestra «Rechazar todo»
banner_preferencestrueMuestra «Personalizar»
banner_close_xfalseMuestra el aspa de cierre
banner_show_logotrueMuestra el logotipo del sitio

«Aceptar todo» y «Rechazar todo» comparten la misma clase de estilo: mismo fondo, mismo borde, mismo grosor, mismo relleno. Es la respuesta a la exigencia de la CNIL / EDPB (directrices 03/2022): rechazar debe ser tan sencillo y tan visible como aceptar.

El aspa de cierre

Está desactivada por defecto, porque un aspa de cierre equivale a un rechazo implícito. Tres comportamientos que conviene conocer:

  1. Cuando banner_close_x está en off, el aspa está presente en el marcado pero lleva el atributo hidden, y el CSS la retira por completo: ni visible, ni enfocable, ni anunciada a los lectores de pantalla.
  2. Cuando está activada, pulsar el aspa ejecuta la ruta «Rechazar todo» completa, nunca un cierre silencioso.
  3. El runtime la revela y la reetiqueta en un solo caso: cuando un rechazo no ha podido registrarse en absoluto. Entonces ya no rechaza nada, simplemente aparta el aviso — y ese cierre no cuenta como una elección. Un visitante cuyo servidor rechaza la decisión no se queda ante un banner que no puede cerrar.

La primera capa

  • El título (text_title) y el mensaje (text_message). El mensaje es el único campo con HTML enriquecido del plugin; se renderiza con un wp_kses limitado a <a href target rel>.
  • El enlace a la política de privacidad: link_privacy_policy, con reserva en la página de política declarada en WordPress si el ajuste está vacío.
  • El enlace a la política de cookies: link_cookie_policy, sin reserva.
  • El nombre del responsable del tratamiento: legal_company_name, y si no, el nombre del sitio, mostrado como «Responsable del tratamiento: …» (art. 13(1)(a)).
  • El logotipo: el logotipo personalizado del tema en tamaño medium, y si no, el icono del sitio.

El panel de preferencias

Una fila por categoría disponible, dentro de un role="group" con nombre.

  • Una categoría forzada muestra una insignia de texto «Always active», sin interruptor.
  • Una categoría opcional muestra un <button role="switch"> con aria-checked, aria-labelledby y aria-describedby. El estado lo transmiten aria-checked, la posición del cursor y una palabra visible On/Off — nunca el color por sí solo.
  • Objetivo táctil ampliado a 44 px en pantalla pequeña (WCAG 2.2 AA, criterio 2.5.8).
  • La barra de acciones del panel es sticky abajo, para que «Guardar mis opciones» siga siendo alcanzable mientras la lista se desplaza.

Si el módulo TCF está activo, aparecen dos bloques adicionales: las finalidades TCF alcanzables y las funcionalidades especiales declaradas. Véase IAB TCF v2.2.

Renovación del consentimiento

AjustePor defectoLímites
consent_renewal_months120 a 13
consent_policy_hash_checktrue

0 no significa «no volver a preguntar nunca». El valor 0 se convierte en 13 meses, y 13 meses es el tope duro (deliberación CNIL 2020-091). La duración efectiva está, por tanto, siempre entre 1 y 13 meses, y es ese mismo valor el que gobierna la duración de la cookie, la comprobación de caducidad en el servidor y la duración publicada en los documentos generados. La constante OWC_COOKIE_TTL que se ve en el código fuente no es más que una reserva.

consent_policy_hash_check vuelve a pedir el consentimiento cuando sus documentos cambian: la cookie lleva una huella de las páginas de política enlazadas, y una diferencia reabre el banner. La huella vale la cadena vacía cuando no hay ninguna página enlazada — eso es lo que permite distinguir «desconocido» de «cambiado» y no volver a molestar a todo un sitio por una comparación vacía.

Los textos

Siete ranuras, todas vacías por defecto y por tanto traducidas automáticamente: text_title, text_message, text_accept_all, text_reject_all, text_preferences, text_save, y floating_button_label para el botón flotante.

En la administración, cada campo muestra su valor por defecto traducido como texto de ayuda: dejarlo vacío conserva el valor por defecto, que sigue el idioma del visitante.

El filtro owc_banner_texts permite sustituir estos textos por código. Se aceptan ranuras adicionales; los valores no escalares o vacíos se descartan para que una llamada torpe no pueda vaciar el banner.

Cómo se registra realmente la elección

El runtime JavaScript es ES5 estricto — sin funciones flecha, sin plantillas de cadena — para funcionar en los navegadores integrados en aplicaciones y en los WebViews antiguos. Conviene conocer su secuencia de escritura, porque es la que explica la mayoría de los mensajes de error.

  1. Reanclaje del origen. Las URL REST vienen de la configuración de WordPress. Si sus visitantes navegan por otro host (www frente a apex, alias, dominio de vista previa, proxy que reescribe Host), esa URL es cross-origin y el navegador se niega a almacenar el Set-Cookie mientras WordPress responde 200. Por eso el runtime reancla la ruta que le da PHP sobre el origen realmente visitado.
  2. Nonce. Se pide fresco a GET /owc/v1/nonce justo antes de la escritura, nunca va incrustado en HTML cacheable, y se envía en la cabecera X-OWC-Nonce. La obtención tiene un tope de 4 segundos, y un nonce ausente, vacío o inalcanzable nunca impide la escritura ni se le comunica al visitante.
  3. page_url explícita en el cuerpo de la petición, para que la fila de registro no dependa de la cabecera Referer, que un plugin, una meta-referrer o un proxy pueden eliminar.
  4. Guardia antibloqueo de 15 segundos con AbortController: la petición se cancela, no solo se ignora, para que un POST tardío no escriba una segunda fila.
  5. Definición del éxito. El POST tiene éxito si, y solo si: la respuesta es ok, el cuerpo es JSON analizable, json.ok === true, y la cookie se relee en el navegador. Una página de caché, un desafío de borde o un WAF servido en 200 no cuentan.
  6. Si el servidor responde cookie_set: false, el runtime escribe él mismo la cookie con los parámetros devueltos y después la relee. Un fallo sigue siendo un fallo: no se publica nada, no se desbloquea nada.
  7. Un único reintento, y solo ante un 403 cuyo código sea owc_bad_nonce, rest_cookie_invalid_nonce o rest_nonce_invalid, y solo si el nonce obtenido es realmente distinto.
  8. Lo que el runtime publica es lo que el servidor ha guardado: las categorías rechazadas por el servidor se restan del estado local.

Mientras el POST no haya tenido éxito, no se desenmascara nada, no se desbloquea nada, y no se emite ninguna señal granted de Consent Mode.

La taxonomía de errores

El runtime distingue nueve causas, cada una con su mensaje visible: network, refused, ratelimit, unexpected, timeout, browser, config, cookie, owc_cookie_not_persisted. El estado HTTP y el código del servidor se colocan en el elemento (data-owc-status, data-owc-code) y se registran una vez con console.warn — nunca se muestran como texto visible. Así, una captura de pantalla de soporte nombra la causa sin necesidad de una traza de red.

La API JavaScript del banner

window.OWCBanner.show();              // abre en modo banner
window.OWCBanner.hide();              // cierra
window.OWCBanner.openPreferences();   // abre el panel de preferencias
window.OWCBanner.openDnsmpi();        // abre el panel con marketing, social
                                      // y preferences ya en OFF (entrada CCPA)
window.OWCBanner.reset();             // borra la cookie en el cliente, envía una retirada,
                                      // y después recarga la página
window.OWCBanner.acceptCategory( 'social' );  // devuelve una promesa resuelta a true
                                              // solo si el servidor lo ha guardado

Un bootstrap más ligero se imprime en <head> con prioridad 1 y expone window.OWConsent: .config, .categories, .geo, .profile, .optOut, .state, .has(cat), .refresh(), .paint(), .gcmSignals(state). Su configuración es filtrable mediante owc_bootstrap_config — pero todo lo que hay ahí es público y lo comparte la caché: no ponga nunca ahí un dato propio de un visitante.

Abrir el panel desde sus páginas

Tres maneras, todas equivalentes:

  • el fragmento de URL #owc-preferences (o #owc-dnsmpi), que se tiene en cuenta al cargar y en hashchange;
  • cualquier elemento con la clase owc-open-preferences o el atributo data-owc-open;
  • el atributo data-owc-dnsmpi="1" para forzar el comportamiento «Do Not Sell».

Se emite un evento owc:consent-changed en cada cambio, con la carga útil enviada en el detalle. Lo escuchan el bootstrap, el botón flotante, el runtime CCPA y el módulo TCF; usted también puede escucharlo.

Límites del banner

  • Todo depende de wp_head() y wp_footer(): un tema que no los llame no obtiene nada.
  • Sin window.fetch y window.Promise no es posible ninguna escritura y se muestra el error browser.
  • Un consentimiento puede registrarse en el servidor sin conservarse en el navegador (dirección del sitio distinta del host visitado, almacenamiento bloqueado, jar lleno). El runtime detecta ese caso, se niega a contarlo como un éxito, muestra un mensaje dedicado y transmite el hecho al servidor en la siguiente escritura.
  • El banner no tiene shortcode: se muestra en todas partes o en ninguna.

El bloqueo automático

Es el módulo que convierte una elección en un efecto real. Reescribe las etiquetas portadoras de rastreadores antes de cualquier consentimiento, para todo el mundo, y deja que el runtime las libere en el navegador.

Dos modos, un solo ajuste

compliance_strict, activado por defecto.

  • Desactivado (modo laxo): solo se conecta el filtro script_loader_tag. Dicho de otro modo, solo se reescriben los scripts registrados con wp_enqueue_script(). Un <script> escrito a pelo en el tema, un embed, un iframe, un píxel <img>: nada de eso se toca.
  • Activado (modo estricto): además del filtro, un búfer de salida captura todo el documento, y el marcado renderizado a través de la API REST también se trata. En administración, si la petición es un admin-ajax.php, un búfer aparte cubre las respuestas que sirven también al front («cargar más», archivos filtrados) — pero únicamente si el manejador ha declarado él mismo Content-Type: text/html.

Lo que nunca se bufferiza

El búfer estricto sale de inmediato para: la administración, las peticiones AJAX, los feeds, robots.txt, los trackbacks, el cron, los favicons, las peticiones REST, las peticiones JSON, la vista previa del personalizador, los sitemaps, y los puntos de entrada wp-login.php, wp-register.php, wp-signup.php, xmlrpc.php, wp-cron.php, wp-trackback.php — comprobados sobre el nombre exacto del script, nunca como subcadena de la URL. Sale también para las peticiones del escáner, que necesitan el HTML en bruto.

Las cinco pasadas

La reescritura se hace mediante expresiones regulares calibradas, nunca con un analizador DOM. Tres salvaguardas enmarcan el conjunto: un documento de más de 8 MB se devuelve intacto, un fallo del motor PCRE devuelve el documento intacto, y si el resultado ocupa menos de la mitad del tamaño original, se devuelve el original. Dicho de otro modo, el modo de fallo del bloqueo es «página servida sin bloqueo», nunca «página rota».

Pasada 0 — enmascarado. Los comentarios HTML, <style> y <textarea> se sustituyen por marcadores basados en caracteres de control, para que ningún patrón posterior encaje dentro de ellos.

Pasada 1 — <script>. Decisión, en este orden:

  1. ya tratado → intacto;
  2. script del propio plugin → intacto;
  3. type="text/plain" o tipo no JS (ld+json, importmap, x-template) → intacto;
  4. URL buscada en src, data-src, data-rocket-src, data-lazy-src, data-litespeed-src, data-cfsrc — los atributos de los plugins de rendimiento quedan por tanto cubiertos;
  5. host en la lista de permitidos → intacto; recurso first-party que no parece un rastreador → intacto;
  6. coincidencia con el catálogo sobre la forma host + ruta; una URL data: o javascript: se descodifica y se juzga como un cuerpo en línea;
  7. una regla clasificada como necessary nunca se bloquea (Stripe.js, reCAPTCHA, Turnstile, cdnjs…): bloquearla no gana ningún cumplimiento y rompe el snippet que la llama;
  8. ninguna coincidencia → blocker_unknown_script_policy, por defecto allow;
  9. si no, reescritura como type="text/plain" con data-owc-cat, data-owc-vendor, data-owc-src y, si el tipo original era particular (module, por ejemplo), data-owc-type para no degradar el script restaurado.

Los cuerpos en línea se comparan primero con las reglas de catálogo de tipo inline_signature, y después con catorce firmas ancladas escritas en el código: fbq(, _fbq.push, gtag(, dataLayer.push(, ga('…, _gaq.push, _paq.push, hjid, clarity(, ttq.load|track|page, snaptr(, twq(, lintrk(, pintrk(. Excepción explícita: un gtag('consent', …) a solas, es decir, una declaración de Consent Mode por defecto sin ningún otro marcador, se sigue ejecutando.

Pasada 2 — <iframe>. Los mismos atributos de URL, más data-original y data-srcset. First-party o lista de permitidos → intacto. necessary → intacto, porque si no un formulario con captcha quedaría imposible de enviar. Sin coincidencia → blocker_unknown_iframe_policy, por defecto block. El iframe bloqueado pasa a src="about:blank" y se envuelve en un sustituto visual que contiene un botón «Aceptar » que desbloquea la categoría correspondiente.

Pasada 3 — <img>, solo píxeles de seguimiento. Regla importante: una línea de catálogo por sí sola nunca basta para neutralizar una imagen. Si la imagen coincide con el catálogo pero su categoría no es ni analytics ni marketing — un CDN de imágenes, un servicio de gravatares, un alojamiento de fuentes — se deja intacta, porque de lo contrario se eliminarían los medios del sitio en lugar de un rastreador. Solo la heurística de etiqueta puede bloquear: once puntos de recogida conocidos (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 una imagen de terceros de 1×1 píxel. Entonces el src se sustituye por un GIF transparente, y se eliminan todos los atributos portadores de URL para que un cargador perezoso no restaure el original.

Pasada 4 — <link>. Condicionada a blocker_block_resource_hints, por defecto true. Trata solo los hosts que ya están en el catálogo, nunca uno desconocido.

  • Las pistas de recursos (preconnect, dns-prefetch, prefetch, prerender, preload, modulepreload) hacia un tercero catalogado se eliminan, no se difieren: una pista abre una conexión TCP+TLS y revela la IP y la huella TLS del visitante, y después no hay nada que restaurar.
  • Una hoja de estilo clasificada como analytics o marketing se neutraliza.
  • Google Fonts y los demás hosts de tipografías clasificados como functional no se tocan nunca: neutralizarlos deja todo el sitio con la fuente de reserva, sin ninguna ganancia de cumplimiento.

Pasada 5 — <object>, <embed>, <source>, <video>, <audio>. Nunca hay bloqueo por defecto: solo se trata a un tercero ya catalogado. Se retiran los atributos autoplay y preload.

Qué cuenta como «first party»

La URL del sitio, la URL de WordPress, la URL de contenido, la URL de los includes, sus equivalentes de red en multisitio, y la base de las subidas. El www. se elimina al comparar. Toda ruta que empiece por /wp-content/ o /wp-includes/ es first-party sea cual sea el host, para cubrir las reescrituras de CDN. Los esquemas que no son HTTP (data:, blob:, javascript:) nunca son first-party.

Contraexcepción: una URL que contenga gtag, gtm.js, analytics, pixel, fbevents, hotjar, matomo, piwik o clarity se trata incluso en el host del sitio. Eso es lo que atrapa un GTM o un Matomo autoalojado, y los proxies first-party.

El runtime de desbloqueo

Se imprime en <head> con prioridad 2. Su configuración JSON solo contiene el nombre de la cookie y la lista de hosts vigilados — nada que dependa del visitante.

  • Lectura de estado: la cookie owc_consent se descodifica hasta estabilizarse, con un máximo de tres pasadas, porque las cookies escritas antes de la 1.4.3 estaban codificadas dos veces.
  • Guardia de los scripts inyectados dinámicamente: el setter HTMLScriptElement.prototype.src y setAttribute se envuelven. Un cargador first-party que asigna s.src = 'https://www.googletagmanager.com/gtm.js?id=…' queda interceptado y el elemento se marca antes de ejecutarse. La lista vigilada tiene veinte hosts: googletagmanager.com, google-analytics.com, googleadservices.com, googlesyndication.com, doubleclick.net, connect.facebook.net, static.hotjar.com, script.hotjar.com, clarity.ms, cdn.matomo.cloud, analytics.tiktok.com, snap.licdn.com, sc-static.net, static.ads-twitter.com, bat.bing.com, s.pinimg.com, cdn.segment.com, js.hs-scripts.com, cdn.amplitude.com, cdn.mxpnl.com. Todo lo que no figure ahí pasa sin ser molestado. Los hosts de su lista de permitidos se retiran de esa lista antes de imprimirla.
  • Reinyección en el orden del documento. Un script externo restaurado bloquea la cola hasta su onload u onerror, con un plazo máximo de 5 segundos para que un proveedor inalcanzable no bloquee el resto. El atributo async solo se aplica si estaba presente en el original — sin eso, un script creado con createElement se forzaría a asíncrono y el snippet de configuración se ejecutaría antes que su biblioteca. Para un script en línea restaurado, document.write se redirige temporalmente para no borrar el documento.
  • Retirada del consentimiento: si una categoría ya aplicada pasa explícitamente a false, el runtime recarga la página. Un script ya ejecutado no se puede descargar (art. 7.3 RGPD). Una categoría simplemente ausente de la carga útil no es una retirada.
  • Un MutationObserver vuelve a escanear el marcado inyectado después de la carga (AJAX, secciones perezosas).
  • Un clic en el botón de un embed bloqueado llama a OWCBanner.acceptCategory(); si la API del banner no existe, el embed se desbloquea localmente, sin persistir nada.

El catálogo de firmas

Tabla {prefix}owc_scripts. El archivo incluido, data/tracker-catalog.json, contiene 175 líneas que cubren 71 proveedores distintos:

RepartoDetalle
Por objetivo110 patrones de URL, 65 nombres de cookies
Por tipo172 fragmentos de texto, 3 expresiones regulares
Por categoríanecessary 55, marketing 37, functional 37, analytics 36, social 10

Columnas: 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.

Dos comportamientos que conviene conocer:

  • Las líneas con match_target = 'cookie' nunca las usa el bloqueo. Sirven únicamente para la tabla de cookies de los documentos generados. Compararlas como subcadena de una URL es precisamente lo que rompía el JavaScript de los sitios.
  • Un patrón de tipo host dirigido a una URL, de menos de seis caracteres y sin punto, se rechaza: «fr» o «IDE» encajarían con «frame.js» y «provider.js».

El catálogo se cachea en la caché de objetos (1 hora) y en un transient (12 horas). Un resultado vacío nunca se cachea. Toda escritura dispara la acción owc_catalog_updated, que vacía esas cachés.

La pestaña Tracker catalogue permite añadir, modificar, eliminar y buscar reglas, con un filtro «solo las reglas que no pueden dispararse»: patrón vacío o invisible, expresión regular que no compila o que retrocede de forma catastrófica, patrón demasiado corto y sin punto para una URL, categoría que ya no existe. Este diagnóstico se hace en PHP, porque solo el motor PCRE puede decir si una expresión compila.

Ajustes del bloqueo

AjustePor defectoValores
compliance_stricttruebooleano
blocker_unknown_script_policyallowallow, block
blocker_unknown_iframe_policyblockallow, block
blocker_block_resource_hintstruebooleano
blocker_allowlist''un host por línea

Puntos de extensión

// Hosts que nunca se bloquean, además de blocker_allowlist.
add_filter( 'owc_blocker_allowlist', function ( array $hosts ) {
    $hosts[] = 'cdn.mi-socio.example';
    return $hosts;
} );

// Filas del catálogo antes de la validación.
add_filter( 'owc_scripts_catalog', function ( array $rows ) {
    $rows[] = array(
        'pattern'      => 'tracker.example.com',
        'pattern_type' => 'host',
        'match_target' => 'url',
        'name'         => 'Ejemplo',
        'vendor'       => 'Ejemplo S. A.',
        'category'     => 'analytics',
    );
    return $rows;
} );

El método público OWC_Blocker::block_html_fragment( $html ) aplica el bloqueo a un fragmento que no ha pasado por el búfer de salida.

Límites del bloqueo

  1. Sin compliance_strict, solo se tratan los scripts encolados por WordPress.
  2. Un script de terceros desconocido se permite por defecto. El bloqueo real se apoya en el catálogo y en las firmas en línea — enriquézcalos.
  3. La guardia de los scripts inyectados por JavaScript solo cubre veinte hosts; un rastreador fuera de esa lista inyectado por código first-party pasa.
  4. Las hojas de estilo clasificadas como functional (fuentes web) se dejan pasar deliberadamente.
  5. Un rastreador catalogado en <img> pero clasificado como algo distinto de analytics o marketing no se neutraliza.
  6. Los medios (object, embed, source, video, audio) nunca se bloquean por defecto: solo se trata a un tercero ya catalogado.
  7. Un documento de más de 8 MB, un fallo de PCRE o una pérdida de más del 50 % del contenido dan una página servida sin ningún bloqueo, en silencio.
  8. La retirada del consentimiento provoca una recarga completa de la página.

El escáner de rastreadores

El escáner inventaría lo que sus páginas cargan realmente. Viene desactivado por defecto: actívelo en la pestaña Scanner.

Tres fuentes de detección

Cada hallazgo conserva su procedencia y la información «¿puede el bloqueo actuar sobre esto?».

1. El HTML servido. El escáner recupera una muestra de las URL de su propio sitio y lee el marcado: <script src> y <script data-owc-src> — así que los scripts ya bloqueados se ven igualmente —, firmas en línea, <iframe>, hojas de estilo externas e imágenes cuya URL contiene 1x1, pixel, track, beacon o impression. No se ejecuta nada de JavaScript: lo que un gestor de etiquetas inyecta en tiempo de ejecución es invisible para esta pasada.

2. Las cabeceras Set-Cookie. Son las únicas cookies que el barrido de servidor puede demostrar, incluidas las cookies HttpOnly que una sonda de navegador nunca verá. Se marcan como no bloqueables: nada en el bloqueo del cliente puede retener una cookie emitida por el servidor. Las cookies del propio plugin se ignoran.

3. La sonda de navegador. Se imprime en el pie de página únicamente para un usuario conectado que tenga manage_options, y solo si el escáner está activado. Fotografía document.cookie al cargar, observa las mutaciones del DOM — eso es lo que atrapa lo que inyecta un gestor de etiquetas —, consulta document.cookie cada 5 segundos para las cookies escritas en el cliente, y transmite sus observaciones cada 2 segundos y en beforeunload. La respuesta que lleva la sonda se marca como no cacheable.

Límite reconocido en el código: un administrador suele haberlo aceptado ya todo. Por tanto, la sonda describe el estado posconsentimiento, no el estado preconsentimiento.

El barrido

La cola de URL se construye para cubrir plantillas, no páginas: primero las URL centrales (portada, página de inicio estática, página de entradas, y las cuatro páginas de políticas enlazadas), y después un entrelazado en carrusel por familia — hasta 30 páginas, 15 entradas, 5 por tipo de contenido público personalizado, 3 categorías y 3 etiquetas. Sin ese entrelazado, treinta páginas casi idénticas se comían todo el presupuesto y nunca se llegaba a las plantillas de WooCommerce.

El filtro owc_scanner_urls permite añadir URL, pero el resultado se vuelve a restringir al host del sitio: el escáner nunca sale de su dominio.

Cada petición se hace siguiendo las redirecciones a mano (dos saltos como máximo, cada salto vuelto a comprobar contra el host del sitio), con una respuesta limitada a 2 MB, una cabecera Cache-Control: no-cache, no-store y un parámetro de URL único para no leer una página de caché, y un user-agent OW-Consent-Scanner/<versión>. Una respuesta que no sea HTML se cuenta como omitida, no como un fallo.

Se envía un secreto de omisión en la cabecera X-OWC-Scanner para que el bloqueo se retire y deje ver el HTML en bruto. Se compara en tiempo constante, y la cabecera se comprueba en primer lugar para que un visitante corriente ni siquiera dispare la lectura de la opción.

Presupuesto, bloqueo, reanudación

RestricciónValor
URL por ejecuciónscanner_max_urls, por defecto 25, límites 1 a 500
Tiempo de espera por peticiónscanner_timeout, por defecto 8 s, límites 1 a 60
Presupuesto de reloj por lotemax_execution_time − 10 s, si no 45 s, acotado entre 5 y 60 s
Bloqueo de ejecución15 minutos
Vida útil de la cola6 horas

Si el presupuesto se agota, se programa una reanudación un minuto más tarde y el barrido continúa donde se había quedado. Un segundo lanzamiento mientras un barrido está en marcha responde «ya hay un escaneo en curso». Cortocircuito: si tres peticiones fallan sin que se haya leído ninguna página, el barrido se detiene en lugar de quemar su tiempo de espera en veintidós URL más — el caso típico de un sitio cuyo loopback HTTP está bloqueado.

Las firmas en línea reconocidas

Veintitrés agujas, agrupadas en veintiuna etiquetas: 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 clave de deduplicación es la firma, nunca una huella del cuerpo: un snippet real de gtag o de Pixel contiene valores propios de la página, lo que daría una línea por página.

El bucle de realimentación: hallazgo → catálogo → bloqueo

Es lo que hace útil al escáner. Clasificar un rastreador escribe una regla en el catálogo de bloqueo. Un clic en la categoría de un hallazgo hace tres cosas: escribe la categoría en la fila, memoriza su decisión manual, y crea la regla correspondiente. Al final de cada barrido completo, se hace la misma operación en masa para lo que el catálogo aún no sabe clasificar.

Reglas de conversión:

  • un hallazgo de tipo cookie se convierte en una regla match_target = cookie sobre el nombre de la cookie;
  • un hallazgo de tipo inline_script escribe una regla solo si se recupera la aguja original. Sin ella no se escribe ninguna regla: una regla construida a partir de la etiqueta «Google gtag (config)» estaría muerta para siempre y encajaría con el nombre del hallazgo, lo que lo haría pasar por «cubierto» eternamente;
  • si no, una regla host sobre el dominio, rechazada si el dominio está vacío.

Sus decisiones manuales se guardan aparte (500 como máximo) y se vuelven a aplicar en cada nueva observación, porque la marca «confirmado» significa también «una regla de catálogo ha encajado» y por tanto no puede llevar sola la información «el administrador lo ha decidido». Reclasificar como «sin categorizar» elimina la entrada: es una marcha atrás de verdad.

El catálogo se compara por el patrón más largo primero, para que una regla amplia (google-analytics.com) no machaque la regla más precisa que usted ha escrito (www.google-analytics.com).

Programación, alertas, retención

scanner_frequency acepta hourly, twicedaily, daily y weekly — pero la administración solo ofrece las recurrencias que su instalación conoce realmente. Por defecto: weekly. Desactivar el escáner desprograma los dos eventos.

Dos correos distintos, nunca los dos a la vez:

  1. Rastreadores sin categorizar — enviado únicamente para identificadores realmente nuevos, con una memoria limitada a 500 entradas.
  2. «El barrido no ha podido leer este sitio» — limitado a un mensaje por semana y por firma de error. Es el modo de fallo más grave, porque un barrido que no lee nada no produce ningún hallazgo y, por tanto, ninguna alerta del primer tipo.

Destinatario: scanner_alert_email, y si no, la dirección de administración del sitio.

Retención: 90 días. Los hallazgos no revisados en 90 días se eliminan, en cuanto se haya leído al menos una página — no solo cuando termina un barrido.

El panel de cobertura

Es la parte más importante de la pestaña Scanner, y alimenta también el recuadro de advertencia de los documentos generados. Responde siempre a estas preguntas:

  • ¿se ha lanzado ya algún barrido?
  • ¿el último terminó, o lo interrumpió su presupuesto de tiempo?
  • ¿cuántas páginas se han recuperado realmente (no retirado de la cola)?
  • ¿cuántas no se han podido recuperar, y cuál fue el primer error?
  • ¿algún navegador ha informado ya de algo, o no se ha observado nunca JavaScript?
  • ¿cuál es la ventana más allá de la cual un rastreador no revisado desaparece?

Un barrido que no ha alcanzado ninguna página se presenta como un fallo, no como un resultado limpio. Es este panel el que hay que leer antes de publicar una política de cookies construida sobre esos hallazgos.

La pestaña Scanner

Tres tarjetas (rastreadores detectados, sin categorizar, último barrido y próxima ejecución), el panel de cobertura, un botón «lanzar un escaneo ahora», el formulario de programación, y después la lista de hallazgos: filtros por categoría, búsqueda de texto, filtro por tipo de elemento (script, inline_script, iframe, stylesheet, pixel, cookie, link, preconnect), filtro por estado, exportación CSV, paginación de 25 en 25. Clasificación individual o en masa — el selector de masa tiene un primer elemento vacío desactivado, para que un envío accidental no se convierta en una desclasificación general.

Ajustes del escáner

AjustePor defectoLímites
scanner_enabledfalsebooleano
scanner_frequencyweeklyhourly, twicedaily, daily, weekly
scanner_max_urls251 a 500
scanner_timeout81 a 60
scanner_probe_modeadminsadmins, off
scanner_alert_email''dirección de email

A tener en cuenta: scanner_probe_mode no se consume en esta versión. El código lo señala explícitamente — la condición de impresión de la sonda ya no lee este ajuste y se basa únicamente en scanner_enabled. Guardar off almacena el valor sin desactivar la sonda. Para detener realmente la sonda, desactive el escáner.

Límites del escáner

  1. El barrido de servidor no ejecuta nada de JavaScript. Sin la sonda, lo que inyecta un gestor de etiquetas no aparece en el inventario.
  2. La sonda está reservada a los administradores conectados y no existe ningún modo que la ejecute para un visitante corriente: eso exigiría su propia información previa.
  3. Una cookie puesta por la cabecera Set-Cookie se detecta pero no es bloqueable.
  4. El barrido nunca sale del dominio y está limitado a 25 URL por defecto: un sitio grande nunca queda cubierto por completo.
  5. En un alojamiento donde el loopback HTTP está bloqueado (autenticación HTTP de preproducción, cortafuegos), el barrido no lee nada.
  6. Un hallazgo no revisado en 90 días desaparece, y por tanto desaparece también de la política de cookies.
  7. El escáner depende de WP-Cron: en un sitio con DISABLE_WP_CRON y sin cron de sistema, el barrido programado no se dispara.

El generador de documentos legales

Los cuatro documentos

TipoContenidoIdiomas disponibles
cookie_policyPolítica de cookiesfrancés, inglés, portugués
privacy_policyPolítica de privacidadfrancés, inglés, portugués
legal_noticeAviso legalsolo francés
termsCondiciones generalessolo francés

Cada documento generado es una página de WordPress, versionada mediante metadatos: _owc_policy_type, _owc_policy_version (incrementada en cada generación), _owc_policy_hash (huella SHA-256 del HTML), _owc_policy_generated_at, _owc_policy_profile, _owc_policy_lang, _owc_policy_manual_edit.

Tras la generación, el enlace se reinyecta en los ajustes correspondientes (link_cookie_policy, link_privacy_policy, link_legal_notice, link_terms), y publicar una política de privacidad actualiza la página de política declarada en WordPress.

Dos cerrojos antes de publicar

Cerrojo 1 — los campos obligatorios. La generación se rechaza con la lista de las claves vacías, en lugar de publicar cláusulas con huecos.

DocumentoCampos exigidos
Todoslegal_company_name, legal_company_email
Salvo la política de cookies+ legal_company_address, legal_country
Aviso legal+ legal_company_phone, legal_publication_director, legal_host_name, legal_host_address, legal_host_phone
Aviso legal, países FR / BE / LU+ legal_company_legal_form, legal_company_reg_number

Cerrojo 2 — el idioma. El generador se niega a publicar un documento redactado en un idioma que la jurisdicción no utiliza. En vista previa, el documento sale con una banda roja de advertencia; en publicación, es un rechazo en redondo. En la pestaña Policies, el botón de generación queda oculto cuando no existe ninguna plantilla para el tipo y el perfil activos, en lugar de mostrarse y fallar sistemáticamente.

Resolución del idioma

PerfilIdioma del documento
quebecfrancés
lgpdportugués
gdpr con legal_country ∈ {FR, BE, LU}francés
Todo lo demásinglés

La advertencia obligatoria

Mientras policy_disclaimer esté activado — y lo está por defecto — cada documento termina con un bloque que dice que es una plantilla generada automáticamente, que debe revisarla un profesional cualificado antes de publicarla, y que lleva la fecha de generación y la versión del plugin. Desactívelo solo con pleno conocimiento de causa.

La tabla de rastreadores — las reglas de honestidad

La tabla publicada en la política de cookies viene de su tabla de escáner, no de una base de terceros. Cinco reglas gobiernan lo que muestra.

  1. Ventana de frescura: solo se publican los hallazgos revisados en los últimos 90 días. Si la columna de última observación aún no existe porque una migración no ha pasado, la ventana se ignora en lugar de publicar «ningún rastreador» en un sitio que sí los tiene — la infradivulgación es la única dirección en la que un documento legal nunca debe fallar.
  2. Recuadro de advertencia al principio de la tabla, derivado del panel de cobertura del escáner: barrido nunca lanzado, ninguna página leída, barrido interrumpido, N URL fallidas, ninguna observación con JavaScript activo, ventana de N días. Un barrido incompleto se divulga, no se publica como un inventario terminado.
  3. Los rastreadores sin clasificar no se ocultan: tienen su propia sección. Son los que nadie ha examinado.
  4. Una cookie observada en una cabecera Set-Cookie se marca con un signo distintivo, con una nota que explica que la deposita el servidor y que ningún bloqueo de cliente puede retenerla. Por tanto, no debe presentarse como condicionada al consentimiento, y la cláusula que afirma que los rastreadores sin clasificar solo se depositan tras el consentimiento lleva la excepción correspondiente.
  5. El nombre del responsable y el enlace a su política vienen del catálogo, únicamente para las reglas que apuntan a una URL. Los patrones de nombre de cookie quedan excluidos: en un documento legal no se afirma quién trata datos sin pruebas.

Las cookies del sitio y las duraciones publicadas

La sección «cookies depositadas por este sitio» enumera las cookies del propio plugin: owc_consent (duración derivada del ajuste de renovación), euconsent-v2 si TCF está activado, owc_geo si la detección regional está activada, owc_gpc (duración de sesión), owc_gpc_notice (5 minutos), más las cookies de inicio de sesión y de ajustes de WordPress.

La tabla de plazos de conservación saca sus cifras de los ajustes realesledger_retention_days, retention_form_data_days, retention_dsar_days y la duración de renovación del consentimiento. No se publica ningún valor decorativo. La duración de la cookie de consentimiento se deriva del mismo cálculo que la propia cookie, lo que garantiza que un ajuste de 0 meses publique «13 meses» y no «nunca».

Detección del tipo de actividad (condiciones generales)

Las condiciones generales tienen 21 secciones, con conmutación vendedor/prestador, productos/servicios, presupuesto/pedido, y cláusulas específicas por tipo de actividad.

legal_business_type (auto, vitrine, rental, ecommerce, services, saas, content) es la fuente de verdad. Cuando vale auto, una heurística examina el sitio — presencia de WooCommerce, registro abierto, plugins de suscripción, página de tarifas, páginas de servicios, y después densidad de palabras clave en los últimos cincuenta contenidos publicados — pero su veredicto nunca se imprime en un documento publicado: solo sirve para elegir las cláusulas opcionales.

Las citas de artículos franceses solo se insertan si legal_country vale FR. Bélgica tiene sus propias citas (CDE art. VI.45 §1, art. VI.47, Servicio de mediación para el consumidor); Luxemburgo y Quebec reciben una formulación neutra. Citar un texto sin verificar sería el defecto, no el remedio.

Otros comportamientos

  • La fecha del documento se formatea en la configuración regional del documento. Si el paquete de traducción correspondiente no está instalado, el formato pasa a dd/mm/aaaa: un documento francés no puede abrirse con «4 September 2026».
  • Edición manual detectada: si el contenido almacenado ya no se corresponde con su huella, se guarda una revisión antes de sobrescribir y la pantalla muestra el distintivo «modificaciones manuales sustituidas», con un enlace a las revisiones.
  • El generador nunca degrada: una página publicada sigue publicada aunque se desmarque la casilla «publicar», y un título o un enlace permanente que usted haya renombrado sobrevive a una regeneración.
  • Inyección en el menú del pie de página: auto_footer_menu_inject, desactivado por defecto. El plugin no modifica su sitio público sin petición explícita. Ubicaciones reconocidas cuando lo activa: footer, footer-menu, footer_menu, footer-1, footer_1, secondary, legal.

Puntos de extensión

// Cuerpo en bruto de la plantilla, con las {{variables}} todavía en su sitio.
add_filter( 'owc_policy_template', function ( $html, $type, $profile ) {
    return $html;
}, 10, 3 );

// Las variables inyectadas en la plantilla.
add_filter( 'owc_policy_vars', function ( array $vars, $type, $profile ) {
    return $vars;
}, 10, 3 );

// El HTML final, con las variables ya sustituidas.
add_filter( 'owc_policy_html', function ( $html, array $vars ) {
    return $html;
}, 10, 2 );

Aportar su propio cuerpo mediante owc_policy_template desactiva el cerrojo de idioma: un sitio que aporta su texto asume su idioma. Es la vía oficial para publicar un aviso legal que no esté en francés.

Hay cuatro bloques pregenerados disponibles como variables reservadas: {{__trackers_table__}}, {{__categories_list__}}, {{__retention_table__}}, {{__jurisdictional_rights__}}. Todas las variables están ya escapadas en el contexto de su uso. Tres métodos públicos son reutilizables por una plantilla de terceros: OWC_Policies::build_cookie_table(), build_data_retention_table() y render_first_party_cookies().

Rutas REST

MétodoRutaParámetrosAcceso
POST/owc/v1/policies/generatetype (obligatorio), publish (booleano, por defecto false)manage_options
GET/owc/v1/policies/previewtype (obligatorio)manage_options

El HTML de la vista previa pasa por wp_kses_post() antes de devolverse.

Límites del generador

  1. Son plantillas, no asesoramiento jurídico. Hágalas revisar.
  2. El aviso legal y las condiciones generales solo existen en francés. Cualquier otra jurisdicción choca con el cerrojo de idioma, salvo que usted aporte su propio cuerpo.
  3. Solo tres idiomas: francés, inglés, portugués.
  4. Cuatro países del EEE tienen autoridad nombrada; en el resto, una formulación genérica.
  5. La tabla de rastreadores vale lo que valga su barrido — y el documento lo dice.
  6. La clase del generador pesa unos 440 KB de plantillas jurídicas: se carga de forma perezosa, únicamente cuando se solicita realmente uno de los tres puntos de entrada.

El registro de consentimiento

El registro es la respuesta al artículo 7.1 del RGPD: poder demostrar que la persona consintió. Está activado por defecto.

La tabla

{prefix}owc_ledger, con todas las fechas en UTC:

ColumnaTipoContenido
idbigintClave primaria
created_atdatetimeMarca de tiempo UTC
visitor_tokenchar(32)Token seudónimo del navegador, 32 caracteres hexadecimales
eventvarchar(20)accept_all, reject_all, save_preferences, withdraw, auto, gpc_opt_out
categoriesvarchar(255)Lista de las categorías concedidas
profilevarchar(20)Perfil de cumplimiento vigente en el momento de la acción
sourcevarchar(60)banner, preferences, footer_link, api, auto
ip_pseudonymousvarchar(45)IP truncada
ua_hashchar(64)Huella salada del user-agent
page_urlvarchar(500)Página donde tuvo lugar la acción
prev_hashvarchar(128)Huella de la fila anterior
row_hashvarchar(128)Huella de esta fila
policies_hashchar(64)Huella de los documentos vigentes
banner_revisionvarchar(40)Huella del banner realmente mostrado
plugin_versionvarchar(20)Versión del plugin en el momento de la escritura

Las tres últimas columnas permiten reconstruir lo que el visitante vio, no solo lo que marcó.

La cadena

Cada fila se firma mediante HMAC sobre una serialización canónica prefijada por longitud de su contenido y de la huella de la fila anterior. La génesis es una secuencia de 64 ceros. La escritura se hace dentro de una transacción, con un bloqueo de fila sobre la última entrada: es ese bloqueo el que serializa realmente las escrituras concurrentes e impide que la cadena se bifurque. Se toma un bloqueo con nombre de MySQL como segunda barrera, en la medida de lo posible: si se deniega, la anexión continúa y se dispara una acción para que usted pueda trazarlo.

Los valores se truncan antes de firmar, para que el valor firmado sea exactamente el valor almacenado.

La honestidad sobre la resistencia a la manipulación

El plugin determina él mismo la procedencia de su clave de firma:

ProcedenciaCondiciónVeredicto
constantOWC_LEDGER_KEY está definidaA prueba de manipulación
wp-configAUTH_KEY y AUTH_SALT están definidas, no vacías, distintas entre sí y sin la frase por defectoA prueba de manipulación
databaseEn cualquier otro casoNo a prueba de manipulación

En el tercer caso, WordPress guarda los salts en la base de datos: cualquiera con acceso a la base puede volver a firmar la cadena. El plugin lo informa en el resultado de verificación y muestra un aviso de administración que lo dice con todas las letras. Se conserva una huella no reversible de la clave, lo que permite distinguir una rotación de salts de una reescritura.

Esa es la razón por la que la sección Instalación recomienda definir OWC_LEDGER_KEY.

La verificación

El botón «Verify the chain now» de la pestaña Audit ledger — o la ruta GET /owc/v1/ledger/verify — recorre toda la cadena en lotes de 500 filas.

Se reconocen tres esquemas de firma: el esquema canónico actual, un esquema histórico anterior, y un esquema sin clave procedente de las primerísimas versiones. Este último produce una huella que cualquiera que tenga la base de datos puede recalcular: nunca se considera válido, se cuenta aparte y se señala como una rotura. Un resumen sin clave no es una prueba.

Roturas informadas: hash_mismatch, chain_break, bad_genesis, unkeyed_rows, table_emptied, tail_truncated, head_mismatch, count_mismatch.

El resultado incluye, entre otros: ok, total, checked, table_total, broken_at, breaks, break_count, partial, legacy_rows, unverifiable_rows, anchor, anchor_ok, key_source, tamper_evident, key_rotated.

El ancla de cabecera

Se conserva un ancla fuera de la tabla: identificador, huella, número de filas, marca de tiempo. Sin ella, eliminar las filas más recientes o vaciar la tabla no dejaría ningún rastro. Se dispara una acción cada vez que el ancla se desplaza, y el código invita explícitamente a replicarla fuera de la base — archivo, syslog, endpoint externo — para hacer detectable una reescritura global.

El plugin se niega a sobrescribir un ancla existente y señala con honestidad que un ancla derivada de la propia tabla solo hace detectables los truncamientos posteriores.

La retención

ledger_retention_days, por defecto 1825 días (5 años), límites 0 a 3650. El valor 0 significa conservación ilimitada.

La poda se ejecuta en su propio cron diario y solo retira un prefijo contiguo: nunca un hueco en mitad de la cadena. La huella de la última fila eliminada se memoriza para que el resto siga siendo verificable. Tope por ejecución: 20 000 filas (40 lotes de 500). Un retraso muy grande de poda se reabsorbe, por tanto, a lo largo de varios días.

Minimización de datos

  • Dirección IP: IPv4 con el último octeto puesto a cero (se conserva el /24), IPv6 truncada al /48 con los 80 bits restantes a cero. El enmascarado se hace sobre la forma binaria; una dirección IPv6 que mapea una IPv4 se trata como IPv4.
  • User-agent: solo se almacena una huella salada, nunca la cadena.
  • page_url: validada contra los hosts del sitio. El llamante manda — si aporta el valor, aunque esté vacío, esa es la respuesta; el Referer solo se consulta si no ha dicho nada en absoluto, y se valida de la misma manera.
  • Token de visitante: 32 caracteres hexadecimales salidos de un generador criptográfico, sin vínculo con ninguna identidad.

El algoritmo

ledger_hash_algo acepta sha256 (por defecto) y sha3-256, intersecados con los algoritmos que su PHP soporta realmente. El ajuste se muestra pero no se puede modificar en la administración: cambiar de algoritmo haría fallar la verificación de todas las filas existentes. Las columnas están dimensionadas con más holgura, pero la elección real se limita a esos dos algoritmos.

La pestaña Audit ledger

Lista paginada de 25 en 25: identificador, fecha local y marca de tiempo UTC en bruto, token de visitante, evento, categorías, perfil, IP seudonimizada, y el eslabón de la cadena (huella anterior → huella actual, truncadas en pantalla, valor completo en un tooltip).

Filtros: token de visitante (32 hexadecimales), evento, perfil, rango de fechas — que se introducen en la zona horaria del sitio y se comparan con las marcas de tiempo UTC realmente almacenadas.

Exportaciones CSV y JSON. El JSON lleva un sobre (formato, algoritmo, procedencia de la clave) para que una autoridad pueda volver a verificar el extracto sin disponer del resto de la cadena. Todas las celdas CSV están neutralizadas contra la inyección de fórmulas de hoja de cálculo.

Consultar el registro mediante la API

GET /owc/v1/ledger acepta page, per_page (1 a 200, por defecto 50), visitor_token, from y to. Los tres últimos permiten responder a una solicitud de acceso (art. 15) sin recorrer toda la cadena. Los límites se leen en UTC, y una fecha con formato AAAA-MM-DD se amplía al día entero. Los filtros realmente aplicados se devuelven en la respuesta: un filtro rechazado por las comprobaciones no debe leerse como «aquí está todo el registro».

Qué ocurre cuando el registro rechaza una escritura

Es el comportamiento más importante de todo el plugin. Si el registro está activado y la escritura de la fila falla, la cookie de consentimiento se anula y la respuesta es un 503. No se almacena nada y no se libera ningún rastreador.

El razonamiento es directo: un consentimiento que no se puede demostrar no debe reclamarse. Véase Solución de problemas para saber cómo proceder.

Puntos de extensión

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 es el hook que hay que usar para replicar el ancla fuera de la base de datos.

Límites del registro

  1. La resistencia a la manipulación es condicional, y el propio plugin lo dice.
  2. Las filas anteriores a la 1.2.0 llevan un resumen sin clave: nunca se vuelven a firmar — volver a firmarlas permitiría a un atacante hacer que se refirmase un historial falsificado — e impiden el veredicto «cadena intacta» mientras sigan ahí.
  3. La retención está limitada a 20 000 filas por ejecución diaria.
  4. El registro no lo elimina el eliminador de datos personales de WordPress: es una cadena de hash, retirarla destruiría la prueba que existe precisamente para sostenerla, y solo contiene un token seudónimo y una IP truncada. Un mensaje explica esta decisión al interesado.

El portal de derechos (DSAR)

El portal permite a una persona ejercer sus derechos desde una página de su sitio. Está activado por defecto, pero solo aparece allí donde usted coloca el shortcode.

El formulario

[owc_dsar_form]

Atributos:

AtributoPor defectoFunción
typesaccess,rectification,erasure,portability,restrict,object,optoutTipos ofrecidos, separados por comas
title«Ejercer mis derechos sobre mis datos personales»Título del bloque
submit_label«Enviar mi solicitud»Etiqueta del botón

Si dsar_enabled está desactivado, el shortcode muestra «el portal está desactivado» y la ruta REST responde 404: la desactivación es realmente del lado del servidor.

La lista que usted pide se interseca con la lista que el servidor acepta: una opción rechazada por el endpoint nunca se le ofrece al visitante.

Los ocho tipos de solicitud

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

withdraw se retira deliberadamente de lo que ofrece el formulario. Retirar el consentimiento de cookies es instantáneo en el panel de preferencias; hacerlo pasar por un procedimiento escrito de 30 días haría la retirada más difícil que el consentimiento (art. 7.3 RGPD). Un enlace bajo el formulario abre directamente el panel de preferencias para ese caso.

El filtro owc_dsar_types es la fuente de verdad única: de él deriva la lista de permitidos del endpoint.

El ciclo completo

1. Envío. POST /owc/v1/dsar. Tres protecciones de servidor, en este orden:

  • Trampa para bots: un campo oculto relleno provoca un rechazo 400 con el mensaje genérico de un envío mal formado — un bot no aprende nada sobre el motivo de su rechazo. Del lado del navegador, el formulario muestra incluso el mensaje de éxito sin enviar nada.
  • Declaración (art. 12.6): la casilla «confirmo que ejerzo un derecho sobre MIS propios datos personales» debe estar marcada, y eso se comprueba en el servidor, no solo en el navegador. Se guarda junto con la solicitud.
  • Plazo mínimo de 3 segundos entre la aparición del formulario y el envío, del lado del cliente.

Límites de peticiones: 3 por hora y por dirección IP, 3 por día y por dirección de email afectada — la dirección la elige el atacante, así que es esa la que hay que limitar — y 30 por hora para todo el sitio.

La fila se inserta con el estado pending, un token de 64 caracteres del que solo se almacena la huella SHA-256, una fecha de caducidad del token y un vencimiento de respuesta. El identificador no se devuelve: sin enumeración, sin fuga de volumetría. La respuesta vale {ok, mail_sent, message}, y mail_sent refleja el fallo real de envío.

Al DPD no se le notifica en esta fase. De lo contrario, un llamante anónimo haría salir dos correos por envío desde su dominio.

2. Correo de verificación. El enlace apunta a su página de inicio con el token en claro como parámetro. Validez: dsar_token_ttl_days, por defecto 7 días, límites 1 a 90. El correo lleva una cabecera Reply-To pero nunca un From reescrito — reescribir el sobre es lo que rompe SPF. La dirección de respuesta es dsar_email, y si no legal_dpo_email; nunca la dirección de administración del sitio, que no debe publicarse.

3. Confirmación de identidad. El enlace abre una página que no ejecuta nada: la confirmación se hace mediante un POST protegido por un nonce. Eso es lo que impide que un escáner de enlaces de correo (Safe Links, URL Defense, vista previa de bandeja de entrada) confirme una identidad en lugar de la persona.

La página es un documento HTML autónomo, servido fuera del tema, con noindex, nofollow y cabeceras no cacheables. Los intentos están limitados a 30 por hora y por IP. Las respuestas son distintas: 404 para un enlace desconocido, 200 para un enlace ya confirmado, 410 para un enlace caducado, 403 para un nonce vencido, 500 para un fallo de escritura, 200 para una confirmación.

4. Lo que dispara la confirmación.

  • El estado pasa a verified.
  • El vencimiento se recalcula a partir de la verificación. Artículo 12.3: el plazo corre desde que la solicitud está completa, no desde un envío nunca confirmado.
  • El token se quema. Su huella se conserva deliberadamente, para que quien vuelva a abrir su enlace lea «ya confirmado» en lugar de un 404 que le pida empezar de cero. El uso único lo garantiza la comprobación de estado.
  • Se registran: la fecha de confirmación, la IP truncada, el token seudónimo del navegador — el único puente posible entre el registro de consentimiento y la solicitud — y el identificador de la solicitud de WordPress asociada.
  • Se abre una solicitud nativa de WordPress con la identidad ya probada, así que directamente en su cola de Herramientas → Exportar / Borrar datos personales. Eso hace que la ejecución cubra todos los plugins del sitio, no solo OW Consent. Correspondencia: access y portability → exportación; erasure → borrado; los demás tipos no abren solicitud nativa.
  • Se notifica al responsable en dsar_email y dsar_notify_email, con reserva en la dirección de administración si ninguna es válida.

5. Ejecución. Un panel encima de la pestaña DSAR requests enumera las 20 solicitudes verificadas, ordenadas por vencimiento, con los días restantes o de retraso.

AcciónComportamiento
Descargar los datos (JSON)Rechazo 409 si la identidad nunca se ha confirmado. El archivo se llama dsar-<id>-<AAAAMMDD>.json
Borrar los datosSe ofrece únicamente para una solicitud de tipo erasure, y solo tras la confirmación de identidad. La solicitud se cierra antes del borrado, porque si no el texto libre de la solicitud en curso sobreviviría a su propia ejecución
+2 mesesPrórroga del art. 12.3, una sola vez, con un motivo de 500 caracteres como máximo. El vencimiento retrocede 60 días, los recordatorios se rearman, y el interesado recibe un correo con el nuevo vencimiento y los motivos
CerrarEstado resolved o rejected, nota obligatoria. No se puede reescribir una solicitud ya cerrada, e imposible marcar como «atendida» una identidad nunca confirmada
Reenviar el enlaceEmite un nuevo token, lo que invalida el anterior

Cinco estados: pending, verified, resolved, rejected, expired.

La pantalla nunca carga el token ni su huella, y no muestra el nombre del solicitante.

6. Vigilancia del plazo. En el cron diario: se recorren hasta 200 solicitudes verificadas por vencimiento, un recordatorio siete días antes, un recordatorio al superarse el plazo, una sola vez cada uno. Un aviso de administración señala las solicitudes fuera de plazo y las que están a menos de siete días.

7. Purga. Los envíos nunca verificados se eliminan cuando caduca su token, y las solicitudes cerradas se eliminan pasados los retention_dsar_days. La cuenta arranca en el cierre, no en el envío.

El paquete de portabilidad

La exportación produce un documento con formato ow-consent/dsar-export, con la marca de tiempo, el sitio, el interesado, la solicitud y los grupos de datos. Llama a todos los exportadores registrados del sitio, pagina hasta 50 páginas por exportador y se detiene a los 20 segundos. El borrado hace lo mismo con los eliminadores, con los mismos límites.

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

Lo que cubren la exportación y el borrado depende, por tanto, de los plugins instalados en su sitio: un plugin que no registre ninguno de estos hooks hay que tratarlo a mano.

Integración con las herramientas nativas de WordPress

  • Exportador registrado bajo la clave ow-consent, con dos grupos: las solicitudes de derechos y el registro de consentimiento.
  • Eliminador:
    1. los envíos nunca confirmados se eliminan — ningún valor probatorio, solo datos personales;
    2. a las solicitudes cerradas se les borran el texto libre y el nombre, y se conserva la fila mínima (fecha, tipo, estado) como prueba (art. 5.2) hasta retention_dsar_days;
    3. una solicitud todavía abierta se conserva, con un mensaje que explica que hay que responderla antes de eliminarla;
    4. el registro de consentimiento no se elimina, por la razón explicada más arriba.
  • El plugin alimenta también el borrador de política de privacidad de WordPress.

La información bajo el formulario

Bajo el formulario, un bloque desplegable contiene la información del artículo 13: identidad y dirección del responsable, contacto de privacidad, finalidad y base jurídica (art. 6.1.c), datos recogidos, destinatarios y plazo de conservación, plazo de respuesta y posibilidad de prórroga, autoridad de control competente, enlace a la política. Filtro: owc_dsar_form_notice( $html, $context ).

La autoridad mostrada viene de la derivación descrita en Los once perfiles de cumplimiento, y solo se acepta si empieza por mayúscula: leer el ajuste en bruto nombraría a la autoridad francesa en un sitio sudafricano, indio, australiano o californiano.

Sin JavaScript, el botón de envío se oculta y un mensaje propone la dirección de contacto — para que el navegador no haga nunca un envío nativo que pondría la dirección del solicitante en la URL, y por tanto en los registros de todos los servidores y proxies atravesados.

Ajustes DSAR

AjustePor defectoLímites
dsar_enabledtruebooleano
dsar_email''contacto público y destinatario de las notificaciones
dsar_notify_email''destinatario adicional
dsar_response_days301 a 30 — nunca más de un mes
dsar_token_ttl_days71 a 90
retention_dsar_days1095 (3 años)1 a 3650

Puntos de extensión

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 );

Límites del portal

  1. La identidad se apoya en un solo factor: el ida y vuelta por email. Cualquier prueba adicional al amparo del art. 12.6 se anota a mano en las notas de la solicitud.
  2. Un envío de correo defectuoso bloquea el ciclo: la respuesta expone mail_sent: false y el formulario muestra un mensaje que remite a la dirección de contacto, pero la solicitud sigue pending y el plazo legal no arranca.
  3. Los recordatorios de vencimiento y la purga dependen de WP-Cron.
  4. La tabla de solicitudes se elimina siempre al desinstalar el plugin, sea cual sea su ajuste. Exporte antes de eliminar.

La exclusión CCPA «Do Not Sell or Share»

Bajo los perfiles estadounidenses, la ley exige un control con nombre, visible, y que ejecute realmente la exclusión.

Dos formas de colocarlo

Shortcode, donde usted quiera:

[owc_dnsmpi]
[owc_dnsmpi label="No vender ni compartir mi información personal" class="mi-enlace"]
AtributoPor defecto
label«Do Not Sell or Share My Personal Information»
classowc-dnsmpi

Inyección automática en el pie de página: ajuste ccpa_inject_footer, activado por defecto, renderizado en wp_footer con prioridad 20.

Qué hace un clic

Un clic ejecuta la exclusión. No abre ningún panel. El runtime pide un nonce fresco y después envía un reject_all con la fuente footer_link y todas las categorías opcionales a false. El enlace pasa a aria-busy="true" y una zona role="status" muestra el estado: «guardando», y después «su exclusión se ha registrado en este navegador», o el mensaje de error.

El código cita la razón: según los reglamentos de desarrollo de la CCPA (§7026(a)(1)), un enlace que se limita a abrir un panel no es un mecanismo conforme.

Reserva sin fetch ni Promise (WebView antiguo, navegador integrado): el clic abre el panel de preferencias con las categorías publicitarias ya puestas en off.

La restricción de caché

El HTML servido es idéntico para todos. Por eso el enlace se renderiza visible si el perfil configurado del sitio es ccpa o us_generic; si no, se renderiza oculto y se revela en el cliente para los visitantes cuya jurisdicción lo exija, según la cookie de detección regional.

Si la detección regional está desactivada y su perfil configurado no es estadounidense, el bloque directamente no se imprime: sin marcado muerto.

Ningún dato de visitante viaja en la configuración JavaScript de este módulo — solo las listas de perfiles, nunca el booleano resuelto.

Los recursos de este módulo se imprimen en <head>, no en el pie de página, para que el rechazo de Consent Mode llegue a gtag antes de que se dispare un gestor de etiquetas. El JavaScript es únicamente ES5.

Límites

  1. El control de un clic exige fetch y Promise.
  2. El enlace solo es obligatorio bajo los perfiles ccpa y us_generic; bajo cualquier otro perfil, ni se muestra ni se revela.
  3. La revelación en el cliente depende de la cookie de detección regional, y por tanto de JavaScript.

Google Consent Mode v2

El módulo está activado por defecto (gcm_enabled).

Las siete señales

Se emiten las siete señales de Consent Mode v2: ad_storage, ad_user_data, ad_personalization, analytics_storage, functionality_storage, personalization_storage y security_storage. Las seis categorías del plugin se asocian a ellas desde una fuente de verdad única, lo que garantiza que el banner, el bloqueo y el bootstrap no puedan divergir. La tabla está en Las categorías de cookies.

Los valores por defecto, antes de todo consentimiento

La llamada default se imprime como marcado estático, y el update se calcula en el navegador: eso es lo que mantiene la página cacheable.

SeñalRégimen opt-inRégimen opt-out
ad_storagedeniedgranted
analytics_storagedeniedgranted
ad_user_datadeniedgranted
ad_personalizationdeniedgranted
personalization_storagedeniedgranted
functionality_storagedenied si el sitio expone la categoría functional, si no grantedgranted
security_storagegranted siempregranted

Los dos juegos se imprimen en la misma página. El runtime elige cuál aplicar según la cookie de detección regional. Sin eso, una página cacheada fijaría la jurisdicción de un visitante estadounidense sobre un visitante europeo.

Los dos ajustes asociados

AjustePor defectoEfecto
gcm_ads_data_redactiontrueOculta los identificadores publicitarios mientras ad_storage esté denegado
gcm_url_passthroughtrueHace circular gclid / dclid por las URL mientras las cookies estén denegadas

A tener en cuenta

security_storage nunca se toca en una actualización: sigue concedido, tal y como prevé la especificación. El panel de control muestra una tarjeta que indica si Consent Mode está activo.


IAB TCF v2.2

El módulo TCF está desactivado por defecto, y exige un identificador que el plugin no puede proporcionar. Lea la sección de límites antes de activarlo.

Activación

AjustePor defectoLímites
tcf_enabledfalsebooleano
tcf_cmp_id00 a 4095
tcf_publisher_countryFRcódigo ISO de 2 letras
tcf_publisher_purposes_li[]lista de finalidades
tcf_special_features[]lista de funcionalidades especiales

El tope de 4095 no es arbitrario: el campo CmpId ocupa 12 bits en la cadena TCF.

Tres casos en los que el módulo se niega a emitir nada

  1. Ningún CMP ID (tcf_cmp_id < 1): sin __tcfapi, sin cadena TC, sin cookie euconsent-v2, sin ruta REST. Un aviso de administración lo explica. El CMP ID debe asignárselo IAB Europe; el plugin no proporciona ninguno. Emitir una cadena con un CmpId 0 sería peor que no emitir nada.
  2. CMP ID superior a 4095: mismo rechazo, con un aviso específico. Un valor truncado a 12 bits designaría a otra CMP — una suplantación de identidad.
  3. Ninguna Global Vendor List en caché: el stub se imprime pero la API responde cmpStatus: 'error' con una cadena vacía, en lugar de inventarse un número de versión de lista.

Se escribe un diagnóstico en una opción (disabled, missing_cmp_id, no_gvl, active), y únicamente en contexto de administración o de cron, nunca en una página pública.

La Global Vendor List

  • Fuente: https://vendor-list.consensu.org/v3/vendor-list.json.
  • Nunca se descarga durante el renderizado de una página. De eso se encarga un cron diario, con una primera ejecución cinco minutos después de la activación; en administración con la caché fría, se encola una única recuperación.
  • Petición: 5 segundos de tiempo de espera máximo, 2 redirecciones, respuesta limitada a 4 MB. Un cuerpo que alcanza el tope se considera truncado y se rechaza.
  • Solo se conservan los campos útiles por proveedor; las pilas se vacían. Si la serialización sigue siendo demasiado grande, se aplican dos niveles de truncamiento: primero las etiquetas y las URL, y después la reducción a las solas finalidades.
  • Conservación: una semana en un transient, con stale-if-error — si falla, se conserva la última copia válida.
  • Ruta pública: GET /owc/v1/tcf/gvl, limitada a 10 peticiones por hora. Sirve la copia en caché con un ETag y un Cache-Control: public, max-age=86400, y gestiona las peticiones condicionales. Si no hay nada en caché, responde 503 con un Retry-After: 300 — nunca una lista sintética. Esta ruta nunca dispara una petición saliente.

Dónde se calcula la cadena TC

En el navegador, no en PHP. Un stub completamente estático se imprime en <head> con prioridad 0: iframe localizador, implementación de window.__tcfapi, relé postMessage, y un codificador de segmento Core en JavaScript. La configuración publicada no contiene ningún dato de visitante — eso es lo que permite seguir siendo compatible con una caché de página. Filtro: owc_tcf_stub_config.

Existe un espejo PHP del codificador, pero no tiene ningún llamante dentro del plugin: está reservado a integraciones y pruebas, con la advertencia de no imprimirlo nunca en HTML cacheable.

Lo que se codifica

Segmento Core únicamente, en base64url sin relleno. La sección de proveedores se codifica o bien como campo de bits, o bien por rangos: se miden los dos tamaños y gana el más pequeño. El índice de proveedores que se transmite al navegador usa un formato compacto propio, con un tope duro más allá del cual el índice se vacía (los consentimientos de proveedores se pierden entonces, y se conserva la versión de la lista).

Correspondencia categorías → finalidades TCF

CategoríaFinalidades
necessaryninguna — fuera del alcance TCF, lo que garantiza que «Rechazar todo» no pueda producir un consentimiento para la finalidad 1
functional1
analytics1, 8, 9, 10
marketing1, 2, 3, 4, 7
preferences1, 5, 6, 11
social1

Filtro: owc_tcf_purpose_map.

Interés legítimo del editor: solo se retienen las finalidades 2, 7, 8, 9, 10 y 11, porque TCF v2.2 prohíbe el interés legítimo para las finalidades 1, 3, 4, 5 y 6. Cualquier otro valor de tcf_publisher_purposes_li se descarta en silencio.

Un proveedor queda consentido en cuanto se concede al menos una de las finalidades que declara bajo base «consentimiento».

Las funcionalidades especiales

Solo se admiten dos entradas: 1 — uso de datos de geolocalización precisa y 2 — análisis activo de las características del dispositivo. Declare las que utilice en tcf_special_features; la lista se reduce a ese catálogo.

Aparecen como casillas de verificación reales en el panel de preferencias. «Aceptar todo» no las marca: exigen un opt-in explícito propio. «Rechazar todo» y «Do Not Sell» las desmarcan. Viven en la propia cadena TC, y se rehidratan al reabrir el panel a partir de la API TCF.

La capa TCF en el panel

Solo se renderiza si el módulo está activo, es decir, tcf_enabled y un CMP ID utilizable. Dos bloques:

  • Finalidades: únicamente las alcanzables mediante una categoría no forzada y nombradas en el catálogo de la lista de proveedores. Un interruptor que volviera solo a su sitio sería peor que ningún interruptor. Cambiar una finalidad escribe en todas las categorías que la declaran, y después la visualización se vuelve a derivar.
  • Funcionalidades especiales declaradas.

No se codifica en la cadena nada que no se haya mostrado.

La API JavaScript

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

window.__owcTcfUpdateState( tcData );        // sustituye los datos y notifica a los oyentes
window.__owcTcfRefresh();                    // recalcula y emite 'useractioncomplete'
window.__owcTcfUiShown(); window.__owcTcfUiHidden();
window.__owcTcfSetSpecialFeatures( [ 1, 2 ] );

Versiones aceptadas: ausente, null, 2, '2', 2.2, '2.2'. Cualquier otro valor da callback(null, false).

gdprApplies

Se decide en el cliente. Si la detección regional está desactivada, el valor es true. Si no, el país del visitante se compara con la lista de los 31 códigos donde el RGPD se aplica en el sentido de TCF (27 países de la UE, más IS, LI, NO, GB y CH). Fallo cerrado: un país desconocido da true.

La cookie euconsent-v2

Se escribe únicamente en el cliente, y únicamente cuando el módulo está listo y el visitante ha actuado. En caso contrario, la cookie se borra. Su duración es la del consentimiento, limitada a 13 meses (recomendación IAB / CNIL). SameSite=Lax, Secure en HTTPS.

Límites del módulo TCF — de lectura obligada

  1. No es una CMP registrada ante IAB Europe. Exige su propio CMP ID y, aun teniéndolo, la política TCF obliga a una CMP registrada a presentar opciones a nivel de finalidad y a nivel de proveedor. Aquí, los interruptores de finalidades siguen a las categorías y no hay ninguna elección a nivel de proveedor: los proveedores están en su derecho de rechazar esta señal. La pantalla de administración lo dice y habla de «modo compatible con CMP (no oficial)», aceptado por la mayoría de los SSP en desarrollo pero rechazado en producción real dentro del EEE. Si los ingresos publicitarios bajo TCF le importan, use una CMP certificada.
  2. Segmento Core únicamente: sin segmento disclosedVendors, allowedVendors ni publisherTC.
  3. No se emite ninguna publisher restriction: el contador correspondiente vale siempre 0 y el objeto de restricciones está vacío.
  4. purposeOneTreatment y useNonStandardTexts valen siempre false, isServiceSpecific vale siempre true — no hay ámbito global — y la pantalla de consentimiento vale siempre 0.
  5. getVendorList ignora el parámetro de versión y devuelve siempre la lista en caché.
  6. Los consentimientos de editor copian las finalidades generales: sin finalidad personalizada.
  7. Solo dos funcionalidades especiales.
  8. El módulo exige un cron operativo y HTTPS saliente. Sin lista en caché, la API responde cmpStatus: 'error' y la ruta pública devuelve 503.

Global Privacy Control

GPC es una señal que envía el navegador — la cabecera Sec-GPC: 1 y la propiedad navigator.globalPrivacyControl. El plugin la respeta por defecto (gpc_honor), pero su tratamiento depende del régimen jurídico, y ese es el punto importante.

Bajo los perfiles estadounidenses: vinculante

La señal se trata como una exclusión universal vinculante bajo los perfiles ccpa y us_generic. Australia queda excluida explícitamente: régimen opt-out, pero que no reconoce el GPC.

La escritura en el servidor solo se produce tras una cadena completa de comprobaciones, en este orden:

  1. gpc_honor está activado;
  2. la señal está presente — la cabecera Sec-GPC, con reserva en X-Sec-GPC porque algunos proxies y CDN renombran la cabecera; solo cuenta el valor exacto 1;
  3. el perfil efectivo es ccpa o us_genericsi no, no se escribe nada;
  4. se trata de una simple consulta de página: sin administración, sin cron, sin AJAX, sin REST, sin XML-RPC, sin WP-CLI, y con método GET;
  5. las cabeceras aún no se han enviado — una cookie que no se puede poner es una decisión que se volvería a registrar en cada petición;
  6. el marcador de sesión owc_gpc está ausente: una vez por sesión de navegación;
  7. el visitante no parece un bot (user-agent vacío, o que contiene bot, crawl, spider, slurp, monitor, uptime, pingdom, headless, preview, curl/, wget, python-, java/, go-http, okhttp, httpclient, libwww, facebookexternalhit);
  8. la elección ya almacenada no satisface ya la señal — si la satisface, solo se marca el navegador;
  9. se respeta el límite de peticiones compartido: 30 por hora y por IP;
  10. el navegador se marca antes de la escritura, para que un fallo no se convierta en un bucle de reintentos.

El evento se registra como gpc_opt_out con la fuente auto. La URL de la página se reconstruye a partir de la URL del sitio y de la ruta solicitada — nunca de la cabecera Host, nunca del Referer.

Alcance: todas las categorías opcionales pasan a false; las categorías forzadas siguen concedidas. Es una lectura amplia de la noción de venta o de compartición.

Transparencia: si el GPC machaca una elección registrada explícitamente, una cookie de cinco minutos dispara en el cliente un aviso en la parte inferior de la pantalla que lo explica, con un botón «gestionar mis preferencias» que abre el panel y un botón de cierre.

Bajo los perfiles RGPD y asimilados: mero indicio

Fuera de los regímenes opt-out estadounidenses, la señal se trata como una indicación, nunca como un consentimiento:

  • se pone el atributo data-owc-gpc="1" en <html>;
  • Consent Mode pasa ad_storage, analytics_storage, ad_user_data, ad_personalization y personalization_storage a denied;
  • todas las categorías opcionales se ponen a false solo en memoria;
  • given sigue siendo falso, el banner sigue mostrándose, y no se registra nada.

Una bandera interna impide que el runtime le anuncie al visitante que ya está «excluido» cuando ni la cookie ni el registro lo dicen. La justificación está escrita en el código: el GPC no es una señal legalmente reconocida bajo el RGPD y ePrivacy, la nLPD suiza, la LGPD, POPIA, PIPL, DPDP ni la Ley 25.

Las dos cookies que pone este módulo

CookieDuraciónFunción
owc_gpcsesiónMarca que la señal ya se ha tratado para este navegador
owc_gpc_notice300 sDispara el aviso «el GPC ha sustituido su elección»

Las dos van con path=/, SameSite=Lax, Secure en HTTPS, y son legibles por script.

Punto de extensión

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

La detección regional

La detección regional aplica a cada visitante el perfil de su jurisdicción en lugar del perfil configurado del sitio. Está desactivada por defecto.

AjustePor defecto
geo_enabledfalse
geo_default_profilegdpr
geo_mmdb_enabledfalse

La cascada de detección

Gana el primer acierto:

  1. Cloudflare (CF-IPCountry) — solo si el sitio ha declarado estar detrás de Cloudflare;
  2. AWS CloudFront (CloudFront-Viewer-Country) — misma condición;
  3. GEOIP_COUNTRY_CODE, la variable que escribe el propio servidor (mod_geoip, ngx_http_geoip), y que por tanto el cliente no puede falsificar; después, si hay un proxy declarado, las variantes en cabecera HTTP;
  4. Base MaxMind local, si geo_mmdb_enabled está activado;
  5. Nada.

Sin una declaración explícita en wp-config.php, no se cree ninguna cabecera HTTP de país: el cliente puede falsificarlas. Véase Instalación para las constantes.

El código de país se valida contra la lista de permitidos ISO-3166-1 alfa-2 oficialmente asignada (unos 249 códigos enumerados en el código). Los códigos de usuario o reservados — XX, ZZ, el T1 de las salidas Tor — se rechazan.

El fallo cerrado

Sin país detectado, el plugin lee geo_default_profile pero degrada a gdpr cualquier perfil opt-out. Un geo_default_profile puesto en ccpa, us_generic o au se ignora, por tanto, para los visitantes no detectados — y la pantalla de administración solo ofrece perfiles opt-in en ese selector, en lugar de brindar una opción que se ignoraría en silencio.

El motivo es directo: sin esa salvaguarda, un sitio configurado en inglés estadounidense pondría a todo visitante del EEE en régimen opt-out, con rastreadores marcados como «concedidos» sin ningún consentimiento.

Sin reserva en la configuración regional del sitio: el idioma de un sitio no dice nada sobre el lugar de su visitante.

La cookie owc_geo

  • Duración 24 horas, path=/, SameSite=Lax, Secure en HTTPS, legible por script — el bootstrap del front la necesita.
  • Contenido: el país, el perfil, una marca de tiempo y un código de autenticación. Nunca una dirección IP.
  • Al leerla: tamaño acotado, país validado contra la lista ISO, marca de tiempo dentro de la ventana, código verificado en tiempo constante. El perfil siempre se recalcula en el servidor a partir del país: un visitante no puede elegir su régimen jurídico.
  • Solo se cachea una detección real. La reserva de «fallo cerrado» nunca se memoriza.
  • Se escribe únicamente en una petición de front-end, y solo si la detección está activada.

La correspondencia país → perfil

PaísPerfil
Los 30 países del EEEgdpr
GB, JE, GG, IMuk_pecr
CHch_nfadp
BRlgpd
ZApopia
CN, HKpipl
INdpdp
AU, NZau
CAquebec
USccpa
Todo lo demásLa reserva, degradada a opt-in

Hong Kong se rige en realidad por su propia ordenanza local; se trata como PIPL por severidad. Nueva Zelanda se trata como Australia. Todo Canadá recibe quebec — gana el régimen más estricto. Estados Unidos recibe ccpa, que hace las veces de perfil para los demás estados.

La restricción de caché — lo que la detección no hace

El perfil geolocalizado no cambia el HTML renderizado. La reescritura del perfil solo se aplica a las dos rutas REST de visitante, /owc/v1/consent y /owc/v1/state. El renderizado de página, las rutas de administración, wp-admin, el cron y WP-CLI conservan el perfil configurado. Toda respuesta que haya consumido la reescritura se marca como no cacheable.

Es el runtime del front el que elige el régimen, en el cliente, a partir de la cookie. Consecuencia asumida: el régimen que se aplica a un visitante depende de JavaScript, y un visitante cuya primerísima página viene de la caché se trata bajo el perfil configurado del sitio mientras la cookie no exista.

El lector MaxMind integrado

El plugin incorpora su propio lector MMDB, escrito en PHP puro, sin dependencia de Composer — un plugin publicado en WordPress.org no puede incorporar el paquete oficial.

  • Solo lectura, solo país. Sin ciudad, sin ASN.
  • Entradas/salidas acotadas: lectura en bloques pequeños, nunca carga en memoria de un archivo de varios megabytes.
  • Nunca lanza una excepción: un archivo ausente, ilegible, truncado, corrupto u hostil da null, y la detección cae en su perfil estricto.
  • Salvaguardas internas sobre la profundidad de recorrido, el presupuesto de descodificación y el tamaño de carga útil. Los metadatos se cachean en un transient cuya clave integra el tamaño y la fecha del archivo: sustituir su descarga mensual invalida la caché por sí sola.
  • registered_country y represented_country se ignoran deliberadamente. Es la forma documentada de los rangos de proxy anónimo, VPN y satélite, donde el país de registro designa el lugar en que el proveedor registró el bloque, no al visitante. Usarlos pondría a un visitante del EEE detrás de un VPN estadounidense en régimen opt-out.
  • Si otro plugin ya carga un lector GeoIP2, se usa como reserva después del lector propio.

Dónde poner el archivo: wp-content/uploads/ow-geoip/GeoLite2-Country.mmdb. La ruta es filtrable mediante owc_geo_mmdb_path, con validación contra el recorrido de directorios.

Límite importante: el plugin no descarga la base de datos y no ofrece ninguna pantalla de subida. Debe obtener el archivo en MaxMind, depositarlo usted mismo y actualizarlo usted mismo. A cambio, no se envía nada a MaxMind: la búsqueda es íntegramente local, la dirección IP sirve solo como punto de búsqueda y se destruye explícitamente después — nunca se registra, nunca se almacena, nunca se escribe en la cookie.

Límites de la detección regional

  1. Desactivada por defecto, y la variante MaxMind exige un archivo que aporta usted.
  2. Las cabeceras de país se ignoran mientras no haya declarado su proxy.
  3. Sin granularidad estado por estado en EE. UU., ni provincia por provincia en Canadá.
  4. El régimen del visitante depende de JavaScript y de la cookie de 24 horas.
  5. El perfil us_generic nunca se asigna automáticamente.

La integración con OW Forms

Alcance exacto: esta integración apunta al plugin hermano OW Forms, y a nada más. No hay ninguna integración con Contact Form 7, Gravity Forms o WPForms.

Ajuste: forms_integration_enabled, activado por defecto, pero la integración no hace nada si OW Forms no está activo.

Lo que aporta

1. El vínculo consentimiento ↔ envío. En cada envío almacenado se escribe una fila en una tabla propia de OW Consent — el esquema de OW Forms nunca se toca — que contiene:

  • el token seudónimo del visitante (32 hexadecimales, nunca la dirección de email);
  • el identificador de la entrada de registro más reciente para ese token;
  • las categorías concedidas y el perfil de cumplimiento vigente;
  • la huella completa de los documentos publicados en ese instante;
  • la marca de tiempo del consentimiento;
  • el estado de la casilla RGPD del formulario y la etiqueta exacta que llevaba.

Este último punto es el más útil en la práctica: puede demostrar no solo que la casilla estaba marcada, sino qué había escrito al lado.

2. La cascada de borrado. Una solicitud de borrado verificada delega la eliminación en la rutina de OW Forms — que elimina también los archivos subidos — y después borra las filas de vínculo. La integración detecta si OW Forms ya ha conectado su propio oyente a la misma acción, en cuyo caso se limita a podar sus vínculos.

3. Las herramientas de privacidad de WordPress. Se registran un exportador y un eliminador, de modo que Herramientas → Exportar / Borrar datos personales cubre los envíos de OW Forms — y el portal de derechos de OW Consent también, ya que recorre esos mismos hooks. La exportación recoge las etiquetas reales de los campos y añade los campos de consentimiento. Todo valor exportado se neutraliza contra la inyección de fórmulas de hoja de cálculo y se trunca.

4. La retención. En el cron diario, los vínculos más antiguos que retention_form_data_days (por defecto 1095 días, límites 1 a 3650) se eliminan en lotes de 500, con un barrido de los huérfanos. Solo se tocan las filas de OW Consent: la retención de los propios envíos pertenece a OW Forms.

Límites

  1. No se escribe ningún evento de envío en el registro encadenado. La fila de vínculo referencia una entrada existente, no crea ninguna — la cadena no debe escribirse desde fuera.
  2. Imposible vincular un envío si OW Forms está configurado para no almacenar los envíos: no hay ninguna fila a la que engancharse.
  3. Sin recuperación retroactiva: el vínculo empieza en el primer envío posterior a la actualización.
  4. La correlación se hace sobre la huella salada de la dirección que calcula OW Forms, con reserva en la dirección en claro que OW Forms también conserva. No es un esquema de conocimiento cero: la dirección sigue en claro en OW Forms, porque hay que poder responder a la persona.
  5. La integración nunca modifica el plugin OW Forms, ni sus tablas ni sus opciones.
  6. Si OW Forms está activo pero es demasiado antiguo para exponer su rutina de borrado, el eliminador responde explícitamente que los envíos no se han tocado.

El botón flotante

Un pequeño botón persistente permite reabrir el panel de preferencias en cualquier momento. Es la exigencia del artículo 7(3) — la retirada debe seguir siendo tan sencilla como el consentimiento — y de la deliberación CNIL 2020-091, que pide un mecanismo accesible desde cada página.

Condiciones de existencia

Exige los dos ajustes: floating_button_enabled y banner_enabled. El panel de preferencias y la API JavaScript viven en el runtime del banner; sin él, el botón sería un control que no hace nada.

Sale también en administración, en un feed, en robots.txt, y si wp_head no se ha disparado.

Opciones

AjusteValoresPor defecto
floating_button_enabledbooleanotrue
floating_button_positionbottom-left, bottom-right, top-left, top-rightbottom-left
floating_button_stylepill, iconpill
floating_button_labeltexto libreCookies

El botón no tiene ajuste de tema propio: toma banner_style, para que las dos superficies sigan la misma paleta.

Comportamiento y accesibilidad

Es un <button> de verdad, así que es accesible por teclado. Lleva aria-haspopup="dialog" y un aria-label traducible pero no configurable («Manage my cookie choices»): solo se ajusta la etiqueta visible. En modo icon, la etiqueta se oculta visualmente pero los lectores de pantalla la siguen leyendo.

Se renderiza con el atributo hidden; es el JavaScript el que decide mostrarlo. Es visible en todos los estados salvo cuando el banner o el panel ya está en pantalla — incluido inmediatamente después de una elección en la página actual.

Un clic abre el panel de preferencias. Si la API del banner no existe, el botón cae en el fragmento #owc-preferences, volviendo a disparar el evento a mano si ya es el fragmento actual.

z-index 99990, oculto al imprimir, soporte del modo de contrastes forzados y de prefers-reduced-motion, y desplazamiento bajo la barra de administración en posición superior.


Shortcodes

El plugin declara dos shortcodes, y solo dos. Ni el banner ni el botón flotante tienen uno: se muestran en todas partes o en ninguna, pilotados por sus ajustes.

[owc_dsar_form] — el portal de derechos

[owc_dsar_form]
[owc_dsar_form types="access,erasure,portability" title="Mis derechos" submit_label="Enviar"]
AtributoPor defecto
typesaccess,rectification,erasure,portability,restrict,object,optout
title«Ejercer mis derechos sobre mis datos personales»
submit_label«Enviar mi solicitud»

Los tipos que se piden se intersecan con los que el servidor acepta. El tipo withdraw nunca lo ofrece el formulario; véase El portal de derechos.

[owc_dnsmpi] — la exclusión CCPA

[owc_dnsmpi]
[owc_dnsmpi label="No vender ni compartir mi información personal" class="enlace-pie"]
AtributoPor defecto
label«Do Not Sell or Share My Personal Information»
classowc-dnsmpi

Un clic ejecuta la exclusión; véase La exclusión CCPA. Si deja ccpa_inject_footer activado, el enlace ya está inyectado en el pie de página y este shortcode solo sirve para colocarlo en otro sitio.

Reabrir el panel desde un menú o un enlace

No hay shortcode para eso, porque basta con un atributo de clase:

<a href="#owc-preferences" class="owc-open-preferences">Gestionar mis cookies</a>
<button type="button" data-owc-open>Mis preferencias</button>
<a href="#owc-dnsmpi" data-owc-dnsmpi="1">No vender mi información</a>

Es el método recomendado para añadir una entrada «Cookies» a su menú de pie de página.


API REST

Todas las rutas viven bajo el namespace owc/v1, en la raíz REST habitual (https://ejemplo.com/wp-json/owc/v1/…). Trece rutas en total: doce registradas por el módulo REST, más la ruta de la lista de proveedores registrada por el módulo TCF.

Rutas públicas

Sin autenticación. Su seguridad se apoya en la comprobación de origen, un token propio del plugin y los límites de peticiones que se describen más abajo.

MétodoRutaParámetrosFunción
GET/nonceaction (por defecto wp_rest)Devuelve un token fresco. Respuesta: {nonce, action, header: "X-OWC-Nonce", ttl: 43200}, con no-store y Vary: Cookie
POST/consentevent (por defecto save_preferences), source (por defecto banner), categories (obligatorio), page_url (por defecto ''), cookie_unreadable (booleano, por defecto false)Registra una elección, pone la cookie y escribe la fila de registro
GET/stateEl estado del propio llamante: {given, categories, profile, at}. Nunca el token de visitante, nunca la IP, nunca la URL de página. no-store + Vary: Cookie
GET/tcf/gvlSirve la Global Vendor List en caché. ETag, Cache-Control: public, max-age=86400, 304 en petición condicional, 503 + Retry-After: 300 si no hay nada en caché
POST/dsartype (obligatorio), email (obligatorio, formato de email), message (por defecto '', 2000 caracteres máx.)Deposita una solicitud de ejercicio de derechos. Respuesta {ok, mail_sent, message}nunca el identificador

Valores aceptados:

  • 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 objeto, 32 entradas como máximo, solo valores escalares
  • action (nonce): wp_rest únicamente

Rutas de administración

Todas exigen la capacidad manage_options.

MétodoRutaParámetrosFunción
GET/ledgerpage (≥1, por defecto 1), per_page (1–200, por defecto 50), visitor_token (32 hex), from, toLee el registro. Los filtros realmente aplicados se devuelven en la respuesta
GET/ledger/verifyVerifica la cadena de extremo a extremo
GET/settingsLos ajustes efectivos
POST/settingscuerpo JSON libreEscribe los ajustes. Respuesta {ok, updated, rejected, settings}
POST/scanner/runLanza un barrido
POST/scanner/ingestcuerpo JSON {page, findings[]}Recepción de las observaciones de la sonda
GET/scanner/findingspage, per_page (1–200, por defecto 50), filter (slug de categoría o unknown)Lista los hallazgos
POST/policies/generatetype (obligatorio), publish (booleano, por defecto false)Genera un documento
GET/policies/previewtype (obligatorio)Previsualiza un documento

/scanner/ingest exige además un nonce wp_rest válido, en la cabecera X-WP-Nonce o como parámetro _wpnonce — porque sendBeacon no puede poner cabeceras.

/settings en escritura solo acepta las claves declaradas como ajustes reales: un filtro puede inyectar claves sintéticas que no lo son. Cada valor se sanea individualmente. Si no hay ninguna clave conocida, se devuelve un 400.

/ledger vuelve a comprobar la capacidad dentro del manejador, como defensa en profundidad frente a un permission_callback filtrado en otra parte.

El modelo de seguridad de las escrituras públicas

La constatación de partida es sencilla: un nonce no puede vivir en HTML cacheable. Por eso /consent y /dsar tienen un control de acceso permisivo, protegido por cuatro capas en un orden que importa.

  1. Comprobación de origen del mismo host. Gratuita, determinista, no consume ningún presupuesto.
  2. Token propio del plugin en la cabecera X-OWC-Nonce — es una prueba, nunca un veto. La decisión de no usar X-WP-Nonce es deliberada: el núcleo de WordPress intercepta esa cabecera antes de cualquier control de acceso de ruta y rechaza la petición entera si no la valida. Un token caducado servido por una caché mataría, por tanto, una escritura que el endpoint habría aceptado sin token alguno.
  3. Fallo cerrado: sin origen y sin token verificado, se devuelve un 403 owc_missing_origin.
  4. Límite de peticiones al final. Si fuera el primero, una mala configuración consumiría dos tokens por clic y acabaría en 429, lo que taparía el error de configuración detrás de un límite de peticiones.

Hosts aceptados: los que WordPress declara (URL del sitio, URL de WordPress, raíz REST) con su gemelo www./apex, más el host de la cabecera Host de la petición actual y su gemelo. «Mismo origen» significa «el origen se corresponde con el host al que se ha conectado el navegador», no «se corresponde con lo que WordPress tiene en la base de datos» — porque si no, todo sitio al que se llegue por un dominio de vista previa, un alias, un nombre de preproducción, un dominio mapeado en multisitio o detrás de un proxy que reescribe Host sería rechazado en silencio, sin posibilidad de autorreparación. Filtro: owc_allowed_request_hosts.

CORS se deja al núcleo de WordPress, deliberadamente.

/nonce rechaza explícitamente la forma JSONP: sin ese rechazo, una página de terceros podría cosechar el token vivo de un administrador de paso mediante un simple <script src>, no sometido a la comprobación de origen.

Los límites de peticiones

CuboPresupuestoVentanaClave
nonce1205 minutosdirección IP
consent305 minutosdirección IP
dsar31 horadirección IP
dsar_email324 horasdirección de email afectada
dsar_global301 horatodo el sitio
gpc301 horadirección IP
tcf_gvl101 horadirección IP
  • Ventana fija, no deslizante: un cubo que recibe tráfico acaba siempre por caducar.
  • La clave almacenada es una huella salada: no hay ninguna dirección IP ni dirección de email en claro en las opciones ni en la caché de objetos.
  • Normalización de IP: IPv4 se conserva, IPv6 truncada al /64 — el cliente controla cada bit del identificador de interfaz, así que una clave completa permitiría crear un presupuesto nuevo en cada petición.
  • Identidad compartida: detrás de un CDN o de un proxy no declarado mediante OWC_TRUSTED_PROXY, todos los visitantes llegan con la misma dirección. El presupuesto se fracciona entonces por visitante, con un contador ampliado sobre la dirección compartida. Es un límite más burdo: declare su proxy.
  • Filtro owc_throttle_max( $max, $bucket, $window ) — devolver 0 desactiva el límite.
  • El limitador falla abierto si la caché de objetos no está disponible: es mejor no limitar que impedir a alguien ejercer sus derechos.

La ruta /consent en detalle

  • headers_sent() se lee lo primero de todo. Si la salida ya ha empezado, la cookie del servidor no se puede poner: la fila se escribe igualmente y la respuesta sigue siendo 200, con cookie_set: false y un descriptor cookie (name, value, ttl, path, samesite, secure) que el cliente pone él mismo.
  • Las categorías que el sitio no ofrece se devuelven en dropped_categories, no se ignoran en silencio.
  • page_url se valida contra los hosts del sitio, con reserva en un Referer validado de la misma forma, y si no la cadena vacía. La clave está siempre presente, para que el registro no vaya a buscar el Referer en bruto.
  • Si el registro está activado y se rechaza la escritura, la cookie se anula y la respuesta es un 503 owc_ledger_write_refused. No se almacena nada, no se libera ningún rastreador.
  • cookie_unreadable es pura telemetría: no se le aplica ninguna comprobación de validación, precisamente para que un campo de diagnóstico no pueda rechazar jamás una escritura de consentimiento. Su único efecto es una nota en el registro de rechazos.

Respuesta tipo: {ok, state, dropped_categories, logged, cookie_set[, cookie]}. El campo logged vale null cuando el registro está desactivado.

Códigos de error

CódigoHTTPSignificado
owc_bad_param400Parámetro inválido
owc_bad_categories400Objeto categories mal formado, demasiado grande o no escalar
owc_bad_email400Dirección de email inválida
owc_bad_dsar400Envío DSAR mal formado (trampa para bots incluida)
owc_dsar_not_attested400Falta la declaración del art. 12.6
owc_no_settings400Ninguna clave de ajuste conocida en el cuerpo
owc_missing_origin403Sin origen y sin token: fallo cerrado
owc_bad_origin403El origen declarado no es un host de este sitio — la respuesta enumera hasta diez hosts aceptados
owc_bad_nonce403Nonce inválido en /scanner/ingest
owc_jsonp_forbidden403Forma JSONP rechazada en /nonce
owc_forbidden401/403Capacidad insuficiente
owc_dsar_disabled404El portal de derechos está desactivado en el servidor
owc_no_template404Ninguna plantilla para este tipo y este perfil
owc_rate_limited429Límite de peticiones alcanzado
owc_consent_failed500Fallo al aplicar el consentimiento
owc_dsar_store_failed500Fallo al escribir la solicitud
owc_gen_failedvariableGeneración de documento rechazada (campos ausentes, barrera de idioma)
owc_ledger_write_refused503El registro se ha negado a escribir — no se registra nada, no se desbloquea nada

El registro de rechazos

Se conservan los veinte últimos rechazos de escritura y se muestran en el panel de control — únicamente si el registro no está vacío. Se distinguen dos naturalezas:

  • una entrada con un estado HTTP es un rechazo de verdad: no se ha almacenado nada y el visitante ha visto un error;
  • una entrada con estado 0 es una advertencia: la elección sí se ha registrado.

El panel muestra uno al lado del otro el host declarado por el origen y la cabecera Host de la petición. Es exactamente lo que difiere cuando sus visitantes navegan por un host mientras WordPress está configurado con otro — la causa más frecuente de un consentimiento que «no se guarda».

Límites de la API

  1. /consent y /dsar aceptan escrituras anónimas por diseño; la protección es el origen más el límite de peticiones, no un nonce.
  2. El limitador de peticiones falla abierto sin caché de objetos.
  3. La cookie de consentimiento es host-only: una cookie puesta para el apex es ilegible desde una página www., y ninguna cabecera CORS puede corregir eso. El remedio es una URL de escritura del mismo origen.
  4. /scanner/ingest solo acepta manage_options: la sonda no puede venir de un visitante.
  5. /settings no tiene nonce propio: es manage_options más la comprobación de nonce por cookie del núcleo.

Referencia de ajustes

Todos los ajustes caben en una única opción, owc_settings, cargada automáticamente puesto que se lee en cada página del front.

Lectura y escritura en PHP

$ajustes = OWC_Core::settings();               // ajustes efectivos (defaults + guardados + filtros)
$perfil  = OWC_Core::setting( 'compliance_profile' );
$bruto   = OWC_Core::stored_setting( 'compliance_profile' ); // ignora la reescritura por visitante

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

update_settings() fusiona solo las claves enviadas sobre lo existente, nunca el conjunto de los valores por defecto. Eso es lo que permite que la fila de la opción siga vacía mientras usted no haya personalizado nada, y por tanto que los textos sigan el idioma del sitio.

El resultado de settings() se memoiza, con una clave basada en el conjunto de las llamadas del filtro owc_settings: un módulo que registre su filtro después de la primera lectura invalida el memo en lugar de ser ignorado. El memo se vacía en cada escritura de la opción.

Valores por defecto

[
    // 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,          // el aspa = rechazo implícito
    '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',

    // Botón flotante
    'floating_button_enabled'   => true,
    'floating_button_position'  => 'bottom-left',
    'floating_button_style'     => 'pill',
    'floating_button_label'     => 'Cookies',

    // Consentimiento
    'consent_renewal_months'    => 12,             // 0 se convierte en 13, no en «nunca»
    'consent_policy_hash_check' => true,

    // Cumplimiento
    '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,

    // Categorías
    '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 años; 0 = ilimitado
    'ledger_hash_algo'          => 'sha256',

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

    // Portal de derechos
    'dsar_enabled'              => true,
    'dsar_email'                => '',
    'dsar_response_days'        => 30,
    'dsar_notify_email'         => '',
    'dsar_token_ttl_days'       => 7,

    // Bloqueo
    'blocker_unknown_script_policy' => 'allow',
    'blocker_unknown_iframe_policy' => 'block',
    'blocker_allowlist'             => '',         // un host por línea
    'blocker_block_resource_hints'  => true,

    // Integración y ciclo de vida
    'forms_integration_enabled' => true,
    'auto_footer_menu_inject'   => false,
    'delete_data_on_uninstall'  => false,

    // Identidad legal
    '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'        => '',            // vacío a propósito: se deriva del país + perfil
    '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,

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

Listas cerradas

Un valor fuera de la lista se devuelve al valor por defecto.

AjusteValores aceptados
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 (intersecados con lo que PHP soporta)
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

Límites de los enteros

AjusteLímites
consent_renewal_months0 a 13 (0 se trata como 13)
dsar_response_days1 a 30
dsar_token_ttl_days1 a 90
ledger_retention_days0 a 3650 (0 = ilimitado)
scanner_max_urls1 a 500
scanner_timeout1 a 60
legal_minor_age13 a 18
retention_form_data_days1 a 3650
retention_dsar_days1 a 3650
tcf_cmp_id0 a 4095

Saneamiento

  • text_message es el único campo con HTML enriquecido; acepta el HTML permitido en una entrada de WordPress, y el banner solo renderiza al final <a href target rel>.
  • Campos multilínea: 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, y cualquier clave terminada en _desc.
  • URL: toda clave con el prefijo link_ o el sufijo _url, más legal_profession_rules.
  • Direcciones de email: toda clave que contenga email.
  • Todo lo demás: texto simple.
  • Un valor no escalar enviado a un ajuste escalar se ignora, y la clave no se escribe.

Dónde se configura cada cosa

PestañaAjustes
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 (el algoritmo se muestra, no se puede modificar)
Scannerscanner_*
DSAR requestsdsar_*

Tablas

TablaContenido
{prefix}owc_ledgerRegistro de consentimiento encadenado
{prefix}owc_dsarSolicitudes de ejercicio de derechos
{prefix}owc_scannerHallazgos del escáner
{prefix}owc_scriptsCatálogo de firmas del bloqueo
{prefix}owc_form_linksVínculos consentimiento ↔ envío de OW Forms

Un ayudante PHP owc_table( 'ledger' | 'dsar' | 'scanner' | 'scripts' ) recalcula el nombre a partir del prefijo actual: úselo dentro de un switch_to_blog(), ya que las constantes quedan congeladas mientras dura la petición.

Otras opciones y transients

Opciones: 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, más las marcas de migración (owc_catalog_sanitized_v2, owc_match_target_migrated_v1, owc_ledger_hmac_migrated_v1, owc_inline_sig_labels_cleaned_v1, owc_scanner_secret_rotated_v1).

Transients: owc_catalog_<version>, owc_invalid_patterns, owc_gvl_cache, owc_gvl_stub, owc_gvl_etag, owc_gvl_retry, owc_mmdb_<huella>, 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, y los contadores de limitación de peticiones.

Constantes reconocidas en wp-config.php

ConstanteEfecto
OWC_LEDGER_KEYClave HMAC del registro, y clave derivada de la cookie de detección regional
OWC_TRUSTED_PROXY / OWC_TRUSTED_PROXIESRangos CIDR de los proxies de confianza; sin ellas, las cabeceras de IP de cliente se ignoran
OWC_GEO_TRUSTED_HEADERScloudflare, cloudfront, proxy o all
OWC_BEHIND_CLOUDFLAREAtajo para la cabecera de país de Cloudflare
OWC_BEHIND_CLOUDFRONTAtajo para la cabecera de país de CloudFront

Los hooks para desarrolladores

Filtros

FiltroFunción
owc_settingsAjustes efectivos. La respuesta debe depender de algo estable durante toda la petición
owc_banner_textsTextos del banner
owc_bootstrap_configConfiguración estática impresa en <head>nunca un dato por visitante
owc_scripts_catalogFilas del catálogo antes de la validación
owc_blocker_allowlistHosts que nunca se bloquean
owc_scanner_urlsURL que hay que barrer (después se vuelven a restringir al host del sitio)
owc_throttle_maxPresupuesto de un límite de peticiones; 0 lo desactiva
owc_allowed_request_hostsHosts aceptados para una escritura pública
owc_allow_headerless_writeLevanta el fallo cerrado en las escrituras sin origen (por defecto false)
owc_geo_mmdb_pathRuta de la base de datos MaxMind
owc_tcf_purpose_mapCorrespondencia categorías → finalidades TCF
owc_tcf_stub_configConfiguración estática del stub TCF
owc_policy_templateCuerpo en bruto de una plantilla — desactiva el cerrojo de idioma
owc_policy_varsVariables de un documento
owc_policy_htmlHTML final de un documento
owc_dsar_typesTipos de solicitud ofrecidos y aceptados
owc_dsar_form_noticeInformación bajo el formulario
owc_dsar_export_bundleContenido del paquete de portabilidad
owc_dsar_show_fulfilment_panelVisualización del panel de ejecución

Acciones

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

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

Internacionalización

El dominio de traducción es ow-consent, con la ruta /languages. El idioma fuente es el inglés. El paquete incluye la plantilla ow-consent.pot y una traducción francesa completa.

Recordatorio del mecanismo descrito más arriba: mientras una etiqueta o un texto del banner no esté personalizado en los ajustes, sigue el idioma del sitio. En cuanto usted introduce su propio valor, se sirve tal cual, sea cual sea el idioma del visitante.

Los cuerpos de los documentos legales no pasan por ese mecanismo: su idioma sigue a la jurisdicción, nunca a la configuración regional del administrador.


Solución de problemas

El banner no se muestra

Recorra la lista en orden:

  1. ¿Está activado banner_enabled? La insignia de la parte superior de la pantalla de administración lo dice.
  2. ¿Su tema llama a wp_head() y a wp_footer()? Sin ellos, el banner se niega a imprimirse en lugar de producir un marcado inerte.
  3. ¿El visitante ya ha elegido? El banner solo se muestra con <html data-owc="none">. Pruebe en navegación privada o con window.OWCBanner.reset().
  4. ¿El visitante tiene JavaScript? Sin él, el banner permanece oculto — deliberadamente — y el bloque <noscript> toma el relevo.
  5. ¿Alguna caché de página sirve una versión anterior a la activación? Vacíela.

El banner vuelve en cada página aunque la elección se haya registrado

Tres causas, por orden de frecuencia.

  1. El host visitado difiere del host configurado en WordPress (www. frente a apex, alias, dominio de preproducción). El navegador se niega entonces a almacenar la cookie mientras WordPress responde 200. Abra el panel de control: el registro de rechazos muestra uno al lado del otro el origen y la cabecera Host. El remedio es servir el sitio en un único host canónico.
  2. consent_policy_hash_check está activado y sus documentos han cambiado — es el comportamiento buscado: al visitante se le vuelve a preguntar tras una modificación de la política.
  3. Está en una versión anterior a la 1.4.3. La cookie estaba entonces codificada dos veces y el navegador no conseguía releerla, mientras que el servidor sí la leía perfectamente. Actualice: las cookies escritas antes de la corrección siguen siendo legibles.

«Su elección no se ha podido registrar» — error refused o 403

Mire data-owc-code en el elemento del mensaje, o el registro de rechazos del panel de control.

  • owc_bad_origin: el origen declarado no es un host reconocido. La respuesta enumera los hosts aceptados. Si a su sitio se llega legítimamente por varios nombres, añádalos con el filtro owc_allowed_request_hosts.
  • owc_missing_origin: sin origen y sin token. Suele ser un plugin de privacidad que elimina las cabeceras, o un proxy que las reescribe.
  • owc_bad_nonce: solo afecta a la sonda del escáner, no a la escritura de consentimiento — el nonce nunca es un veto en /consent.

Error ratelimit — 429

Los límites son deliberadamente bajos en las escrituras públicas. Dos causas:

  1. Un CDN o un proxy no declarado: todos sus visitantes llegan con la misma dirección y comparten el mismo presupuesto. Declárelo con OWC_TRUSTED_PROXY en wp-config.php.
  2. Una prueba automatizada que envía más de 30 escrituras en 5 minutos desde la misma dirección.

Un 503 owc_ledger_write_refused

El registro se ha negado a escribir, así que no se ha registrado nada y no se ha liberado ningún rastreador. Es el comportamiento buscado: un consentimiento que no se puede demostrar no debe reclamarse.

  1. Compruebe que la tabla {prefix}owc_ledger existe. La pestaña Audit ledger muestra un mensaje legible de «todavía no instalado» en lugar de una pantalla en blanco.
  2. Compruebe los permisos de escritura del usuario de MySQL.
  3. Como último recurso, desactive temporalmente ledger_enabled para restablecer el servicio — sabiendo que mientras tanto pierde la prueba.

Todavía se cargan rastreadores antes del consentimiento

  1. ¿Está activado compliance_strict? Sin él, solo se tratan los scripts encolados por WordPress.
  2. ¿Está el rastreador en el catálogo? Un script de terceros desconocido se permite por defecto. Lance un barrido y clasifique el hallazgo: eso escribe la regla de bloqueo.
  3. ¿Lo inyecta JavaScript first-party? La guardia dinámica solo cubre veinte hosts. Añada una regla de catálogo sobre el dominio del rastreador.
  4. ¿Es una cookie puesta por la cabecera Set-Cookie? Ningún bloqueo de cliente puede retenerla. Hay que tratarla en el origen, del lado del servidor.
  5. Ponga blocker_unknown_script_policy en block — y después pruebe el sitio con cuidado, porque ese ajuste bloquea a todo tercero no reconocido.

Una integración legítima está bloqueada

Añada su host a blocker_allowlist, uno por línea, o mediante el filtro owc_blocker_allowlist. Compruebe también blocker_unknown_iframe_policy: los iframes desconocidos se bloquean por defecto, y es el caso más frecuente.

Si la integración debe seguir condicionada al consentimiento, clasifíquela mejor en la categoría correcta: el sustituto visual lleva un botón «Aceptar…» que la desbloquea con un clic.

El escáner no encuentra nada

  1. ¿Está activado? Viene desactivado por defecto.
  2. ¿Funciona WP-Cron? Con DISABLE_WP_CRON y sin cron de sistema, el barrido programado no sale nunca. Lance uno a mano para comprobarlo.
  3. ¿Es posible el loopback HTTP? Una autenticación HTTP de preproducción o un cortafuegos bloquean el barrido. El panel de cobertura muestra el primer error.
  4. El barrido de servidor no ejecuta nada de JavaScript. Visite una página del sitio como administrador conectado para que la sonda informe de lo que inyecta un gestor de etiquetas.

En todos los casos, lea el panel «lo que este barrido cubre realmente» antes de sacar conclusiones: distingue «no se ha encontrado nada» de «no se ha leído nada».

La generación de un documento se rechaza

Dos causas posibles, y el mensaje lo dice:

  • Hay campos obligatorios vacíos — se muestra la lista de las claves que faltan. Complete la pestaña Legal identity.
  • Barrera de idioma — está pidiendo un aviso legal o unas condiciones generales para una jurisdicción no francófona. En ese caso el botón está oculto. La única vía es aportar su propio cuerpo mediante el filtro owc_policy_template.

El correo de verificación DSAR no llega

Casi siempre es la entregabilidad, no el plugin.

  1. La respuesta de la API lleva mail_sent: false cuando el envío ha fallado, y el formulario muestra entonces un mensaje específico que remite a la dirección de contacto.
  2. Instale un plugin SMTP. La función mail() de PHP la rechazan la mayoría de los servidores de recepción.
  3. Compruebe el SPF, el DKIM y el DMARC de su dominio. El plugin nunca reescribe la dirección de envío — eso es lo que rompería el SPF — solo pone un Reply-To.
  4. Compruebe que dsar_email es una dirección válida.

Mientras el correo no se reciba y se confirme, la solicitud sigue pending y el plazo legal no arranca.

El módulo TCF no se activa

Abra la pestaña Compliance: un aviso de administración nombra la causa.

  • missing_cmp_id: tcf_cmp_id vale 0. Debe obtener un CMP ID en IAB Europe; el plugin no proporciona ninguno y se niega a emitir una cadena con el identificador 0.
  • CMP ID superior a 4095: el valor no cabe en el campo de 12 bits de la cadena y designaría a otra CMP.
  • no_gvl: la Global Vendor List aún no se ha descargado. Llega por un cron diario; compruebe que WP-Cron funciona y que su servidor puede alcanzar vendor-list.consensu.org por HTTPS saliente.

Todos los visitantes reciben el perfil configurado, pese a la detección regional

  1. ¿Está activado geo_enabled? Viene desactivado por defecto.
  2. ¿Ha declarado su proxy? Sin OWC_BEHIND_CLOUDFLARE, OWC_BEHIND_CLOUDFRONT o OWC_GEO_TRUSTED_HEADERS en wp-config.php, las cabeceras de país se ignoran, porque se pueden falsificar.
  3. ¿Está el archivo de MaxMind en su sitio? El plugin no lo descarga y no ofrece pantalla de subida.
  4. Puede ser normal: la primera visualización de un visitante servida por la caché usa el perfil configurado mientras la cookie de 24 horas no exista. El panel de control muestra un bloque de diagnóstico con el país detectado, el perfil aplicado y la fuente.

La administración avisa de que la clave del registro vive en la base de datos

Es el aviso notice_ledger_key. Significa que AUTH_KEY y AUTH_SALT no están en wp-config.php — WordPress las guarda entonces en la base de datos — y que OWC_LEDGER_KEY tampoco está definida. La cadena se sigue construyendo, pero cualquiera con acceso a la base puede volver a firmarla: la afirmación de resistencia a la manipulación ya no se sostiene.

El remedio es añadir OWC_LEDGER_KEY (y, ya puestos, AUTH_KEY y AUTH_SALT) a wp-config.php. La verificación distingue después una rotación de clave de una reescritura, así que este cambio no invalida su historial.

El plugin no hace absolutamente nada, con un aviso rojo

Su WordPress es anterior a la 6.2. El plugin se niega a arrancar, y el aviso dice explícitamente que no se bloquea nada y que no se registra ningún consentimiento. Actualice WordPress, o desactive el plugin y retire el banner de sus páginas mientras tanto.

Hay reglas de catálogo que «no pueden dispararse»

Un aviso de administración las señala, y la pestaña Tracker catalogue tiene un filtro específico. Cuatro motivos posibles: patrón vacío o compuesto únicamente de caracteres invisibles, expresión regular que no compila o que retrocede de forma catastrófica, patrón demasiado corto y sin punto para apuntar a una URL, o categoría que ya no existe en el sitio. Corrija o elimine esas líneas: dan una falsa impresión de cobertura.


FAQ

¿El plugin funciona detrás de una caché de página? Sí, y toda su arquitectura está construida en torno a esa restricción. El HTML producido es idéntico para todos los visitantes; el consentimiento se lee en el navegador y se aplica antes del primer pintado. No se imprime ningún nonce en HTML cacheable. Solo las respuestas realmente personales se marcan como no cacheables.

¿Sustituye a una CMP de pago? En la mayoría de los sitios cubre el mismo terreno: banner, bloqueo, registro de auditoría, solicitudes de derechos, enrutado geográfico y documentos generados. Dos cosas que no hace: no es una CMP registrada ante IAB Europe, y no ofrece asesoramiento jurídico ni documentos revisados — el generador produce borradores que usted debe hacer validar.

¿El registro es a prueba de manipulación? Es a prueba de manipulación en el sentido de que la delata, bajo una condición. Cada fila se firma con HMAC sobre la huella de la anterior: una fila modificada o eliminada rompe la cadena y la verificación dice dónde. La garantía se apoya en que la clave de firma viva fuera de la base de datos. Es el caso si AUTH_KEY y AUTH_SALT están en wp-config.php, o si usted define OWC_LEDGER_KEY. Si no, WordPress guarda los salts en la base y un atacante que acceda a ella podría volver a firmar la cadena — el plugin detecta esa situación y le avisa.

¿El escáner ralentiza el sitio? No. Viene desactivado por defecto y, una vez activado, se ejecuta en WP-Cron a la frecuencia que usted elija, recuperando una muestra de sus propias páginas desde el servidor. Nunca se ejecuta durante la visita de un visitante.

¿El escáner encuentra las cookies? Parcialmente, y es importante entenderlo. El barrido de servidor lee las cabeceras Set-Cookie de sus páginas: encuentra, por tanto, las cookies puestas por el servidor, incluidas las HttpOnly. No ejecuta nada de JavaScript, así que las cookies escritas por scripts en el navegador no le son visibles; esas las recoge una sonda que solo se ejecuta para un administrador conectado. Trate el resultado como un inventario de lo que se ha visto, no como una lista exhaustiva — las dos pantallas lo dicen en lugar de dejar que usted suponga lo contrario.

¿Cómo se ejecuta una solicitud de derechos? La persona envía el formulario y recibe un correo de verificación. Confirmar la solicitud en la página enlazada arranca el plazo del artículo 12.3 y abre una solicitud nativa de WordPress. Desde la pantalla DSAR, usted descarga una exportación JSON producida por todos los exportadores registrados en el sitio, lanza todos los eliminadores, y cierra con una respuesta escrita que queda adjunta a la solicitud. Lo que cubre la exportación depende, por tanto, de los plugins instalados; los que no registren ninguno de estos hooks hay que tratarlos a mano.

¿Puedo usar el módulo TCF para AdSense o Ad Manager? Solo con su propio CMP ID emitido por IAB Europe, y aun así con una reserva. Sin CMP ID, el módulo no se carga en absoluto. Con un CMP ID, emite una cadena TC correctamente codificada y el panel muestra cada finalidad y cada funcionalidad especial que esa cadena puede reclamar — pero la política TCF exige a una CMP registrada opciones a nivel de finalidad y a nivel de proveedor. Aquí los interruptores de finalidades siguen a las categorías y no hay ninguna elección de proveedor: no es una CMP registrada, y los proveedores están en su derecho de rechazar su señal. Si los ingresos publicitarios bajo TCF le importan para su sitio, use una CMP certificada.

¿En qué idioma se generan los documentos? La política de privacidad y la política de cookies existen para los once perfiles, en el idioma de la jurisdicción: inglés, francés para Francia, Bélgica, Luxemburgo y Quebec, y portugués para Brasil. El aviso legal y las condiciones generales solo existen en francés; para cualquier otra jurisdicción, el generador se niega a producirlos en lugar de publicar un documento inadecuado.

¿Qué puedo cambiar en el banner? Cuatro posiciones, un tema claro, oscuro o automático, un logotipo opcional, las etiquetas, y un botón flotante de reapertura. Cualquier elemento con la clase owc-open-preferences reabre el panel. «Rechazar todo» se renderiza con la misma prominencia que «Aceptar todo». El aspa de cierre viene desactivada; activada, registra un rechazo completo, nunca un cierre silencioso. El panel se puede usar con el teclado, sus interruptores llevan un estado visible, y la modal atrapa y después devuelve el foco. Los textos son traducibles y sustituibles mediante el filtro owc_banner_texts.

¿Qué bloquea exactamente el bloqueo automático? Los scripts de terceros, los snippets de seguimiento en línea, los iframes, los píxeles de medición, las hojas de estilo de analítica y marketing, y las fuentes de medios de terceros se reescriben antes del consentimiento y se liberan después, en el orden del documento. Las pistas de recursos hacia un tercero catalogado se eliminan en lugar de diferirse, ya que una pista abre una conexión y no tiene nada que restaurar. Las hojas y las fuentes servidas por un host clasificado como funcional se dejan intactas. Los atributos de carga perezosa de los plugins de caché se neutralizan para que un cargador no restaure una URL bloqueada. El plugin incluye 175 firmas, editables desde la pantalla Tracker catalogue y ampliables por filtro.

¿Qué se inscribe en el registro? Cada acción de consentimiento — aceptación, rechazo, guardado parcial, exclusión GPC, renovación, retirada — se añade a la tabla encadenada. Una fila contiene una IP seudonimizada, una huella del user-agent, la URL de la página, el perfil de cumplimiento, una huella de los documentos vigentes, una huella del banner realmente mostrado y la versión del plugin — los elementos que permiten reconstruir lo que el visitante vio.

¿Qué protecciones tiene el formulario de derechos? Una trampa para bots, un tiempo mínimo de cumplimentación, una declaración obligatoria comprobada en el servidor, tres límites de peticiones distintos, un token de un solo uso almacenado únicamente como huella, y una confirmación que exige una acción explícita en la página — de modo que un escáner de enlaces de correo no pueda confirmar una identidad en lugar de la persona. Una solicitud cuyo autor nunca ha confirmado su identidad no se puede exportar, ni borrar, ni marcar como atendida, y esa comprobación se aplica en el servidor, no solo ocultando botones.

¿Qué señales de Google Consent Mode v2 se emiten? Las siete: ad_storage, ad_user_data, ad_personalization, analytics_storage, functionality_storage, personalization_storage y security_storage. Las seis categorías se asocian a ellas desde una fuente de verdad única, de modo que el banner, el bloqueo y el bootstrap no pueden divergir.

¿Cómo se gestionan la exclusión CCPA y el GPC? Un control «Do Not Sell or Share» se inyecta en el pie de página o se coloca por shortcode. El primer clic registra la exclusión, como exigen los reglamentos californianos, en lugar de abrir un panel. La señal Global Privacy Control se trata como una exclusión vinculante bajo los perfiles estadounidenses, una vez por sesión de navegación, y se avisa al visitante si ha sustituido una elección que él había registrado. Bajo los perfiles de la familia RGPD, el GPC se trata como un indicio: las categorías opcionales quedan prerrechazadas en la interfaz, el banner sigue mostrándose, y no se registra nada — porque ahí el consentimiento debe ser un acto positivo.

¿Qué cookies deposita el propio plugin? Todas son cookies internas, ninguna sirve para el seguimiento, y están declaradas en la política de cookies generada: owc_consent (las elecciones, el token seudónimo del navegador y el perfil; duración según el ajuste de renovación, 12 meses por defecto, con tope de 13), owc_geo (país y perfil detectados, 24 horas, sin ninguna dirección IP), owc_gpc (marcador de sesión), owc_gpc_notice (5 minutos), y euconsent-v2 (cadena TCF, únicamente si el módulo está activado y tras una acción del visitante).

¿Funciona en multisitio? Sí. Cada sitio de la red tiene sus propias tablas y sus propios ajustes. Una activación de red recorre todos los sitios solo si la red tiene como máximo 200; por encima de esa cifra, cada sitio se aprovisiona en su primera petición.

¿Y si elimino el plugin? El registro, los hallazgos del escáner, el catálogo, los vínculos con OW Forms, las opciones y los ajustes sobreviven por defecto. Dos cosas se van siempre, sea cual sea su ajuste: las seis tareas programadas, y la tabla de solicitudes de derechos — es la única que contiene datos personales directamente identificativos de terceros, y una vez el plugin ha desaparecido, ya nada acota su retención y ninguna pantalla permite responderlas. Exporte sus solicitudes antes de eliminar el plugin. Para una limpieza completa del resto, active «Delete all data on uninstall» antes de eliminarlo.

¿Dónde encontrar el soporte?