Why WordPress sites leak trackers
A typical WordPress site does not have one place where trackers live. The analytics plugin prints gtag.js, the theme adds a font service, a form plugin loads a captcha, and editors embed YouTube videos in posts. Each of them prints its scripts in wp_head or wp_footer, usually without asking anyone. A cookie banner plugin that only shows a notice leaves all of that running.
cookieowl solves it from the front of the queue. Printed as the very first thing in wp_head, it sets the Consent Mode defaults and starts watching the page, so every tracker printed after it is held until the visitor accepts its category.
Install with one hook
Add this to the functions.php of a child theme, or save it as a file in wp-content/mu-plugins/ so a theme update never removes it. Priority 1 makes it print before the scripts that WordPress and plugins enqueue:
<?php
/**
* Plugin Name: cookieowl
* Description: Cookie consent for LGPD and GDPR that blocks trackers until consent.
*/
add_action('wp_head', function () {
$policy = esc_url(get_privacy_policy_url() ?: home_url('/privacy/'));
echo '<script src="https://cdn.jsdelivr.net/npm/cookieowl@0.1.0/dist/cookieowl.js"'
. ' data-lang="auto" data-policy-url="' . $policy . '"'
. ' data-consent-mode data-auto-block></script>' . "\n";
}, 1);get_privacy_policy_url() returns the page you chose under Settings, Privacy, so the banner links to it automatically. With data-lang="auto" the banner follows the site language: a site set to Português do Brasil gets the LGPD text.
Holding scripts printed by plugins
Auto mode covers the well known trackers by URL. For any other script a plugin enqueues, WordPress has a filter that lets you rewrite its tag. Find the script handle (it is in the id attribute, without the -js suffix) and mark it:
add_filter('script_loader_tag', function ($tag, $handle) {
$categories = [
'my-chat-widget' => 'preferences',
'my-ab-testing' => 'analytics',
'partner-tracking' => 'marketing',
];
if (isset($categories[$handle])) {
$tag = str_replace(
'<script ',
'<script type="text/plain" data-cookieowl="' . $categories[$handle] . '" ',
$tag
);
}
return $tag;
}, 10, 2);The new type comes first in the tag, and when an element has the same attribute twice, browsers keep the first one, so this also works on themes that still print type="text/javascript".
YouTube and Vimeo in posts
The block editor turns a YouTube link into an iframe. With auto mode, cookieowl swaps it for a placeholder that explains the video sets cookies, with a button to load it once and another to allow marketing cookies. For other embeds, such as maps, add the attributes in a Custom HTML block:
<iframe data-cookieowl="marketing" data-cookieowl-name="Google Maps"
data-cookieowl-src="https://www.google.com/maps/embed?pb=YOUR_EMBED_CODE"
width="600" height="400" title="Map to our shop"></iframe>Questions
Do I need a plugin to add cookieowl to WordPress?
No. A few lines in your child theme functions.php (or a small must use plugin) print the script tag at the top of the head. A header code plugin also works if it can print before other scripts.
Will it block Google Analytics added by Site Kit or another plugin?
With data-auto-block, cookieowl recognizes gtag.js, Tag Manager and the other known trackers by URL, even when a plugin prints them, as long as cookieowl is printed before them.
Does it work with page caching plugins?
Yes. The banner is rendered in the browser and the choice is a first party cookie, so cached pages are the same for everyone. Exclude the script from JavaScript minification and combining so it stays first.
What about embedded YouTube videos in posts?
Auto mode replaces YouTube and Vimeo iframes, including the ones the block editor embeds, with a click to load placeholder until marketing cookies are accepted.