You’ve installed a cookie banner. It looks right, the visitor clicks Reject, and Google Analytics loads anyway. Or the opposite: they click Accept, and nothing is tracked until they open a second page, so you lose every visitor who only reads one.
Both problems come from the same place: the banner and the tracking code don’t talk to each other. This post shows three ways to connect them, from “no code” to a small snippet that works with any banner. You don’t need any particular plugin for any of it.
This is a technical guide, not legal advice. What your site needs depends on where your visitors are and what you track.
What “after consent, no reload” means
In the EU and UK, analytics and advertising cookies need the visitor’s consent first. For your scripts, that means three things:
- Before a choice, Google Analytics and the Meta Pixel don’t load at all. No request to Google or Meta.
- The moment they click Accept, the scripts load on the same page, without a reload. Otherwise the landing page view, often the most important one, is never counted.
- On every later page, the scripts load straight away, because the choice is remembered.
Google calls the first approach basic consent mode: the tags wait until the visitor agrees. (In advanced consent mode, the Google tag loads before consent and sends cookieless pings. That’s a separate decision, and this post doesn’t cover it.)
Option 1: Let the banner load the scripts
The simplest fix is to stop loading GA and the Pixel yourself and let the consent tool do it.
- Complianz asks for your GA measurement ID in its wizard and loads the tag itself, after statistics consent. Other scripts go into its Script Center, with a category.
- CookieYes and Cookiebot can scan your site and block known scripts automatically until the right category is accepted.
If you go this way, remove every other copy of the tag: the theme’s “header scripts” box, Site Kit, a WooCommerce extension, an old snippet. Duplicate tags are the most common reason GA still loads after Reject: the banner blocks its own copy and another plugin prints a second one.
Option 2: Mark the script so the banner holds it back
If you’d rather keep the tag in your theme or snippets plugin, you can mark it so the consent tool treats it as blocked. Each tool uses its own attribute (from their documentation, as of October 2026):
| Tool | Statistics (Google Analytics) | Marketing (Meta Pixel) |
|---|---|---|
| Complianz | type="text/plain" data-category="statistics" | type="text/plain" data-category="marketing" |
| CookieYes | data-cookieyes="cookieyes-analytics" | data-cookieyes="cookieyes-advertisement" |
| Cookiebot | type="text/plain" data-cookieconsent="statistics" | type="text/plain" data-cookieconsent="marketing" |
type="text/plain" is the trick: the browser doesn’t run a script whose type isn’t JavaScript. When the visitor accepts, the consent tool switches it back and runs it.
For Google Analytics with Complianz, both parts of the tag need the marking:
<script type="text/plain" data-category="statistics" async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script type="text/plain" data-category="statistics">
window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push( arguments ); }
gtag( 'js', new Date() );
gtag( 'config', 'G-XXXXXXXXXX' );
</script>
The downside: the snippet is tied to one banner. Switch from Cookiebot to Complianz, and every tag needs new attributes. Forget one, and it either never runs or always runs.
Option 3: One snippet for any banner (the WP Consent API)
The WP Consent API is a free plugin that gives every consent tool and every tracking script one shared language. Complianz, CookieYes and Cookiebot are all listed as supporting it on its WordPress.org page, as of October 2026, along with about twenty others. Install it, check that your banner’s integration is on, and your code only has to ask one question: wp_has_consent( 'statistics' ).
It also fires an event, wp_listen_for_consent_change, the moment the visitor makes a choice. That’s what makes “no reload” possible.
Here’s the snippet. Put it in a child theme’s functions.php, a must-use plugin, or a snippets plugin, and replace the two IDs:
<?php
/**
* Load GA4 and the Meta Pixel only after cookie consent,
* through the WP Consent API (works with any banner that supports it).
*/
add_action(
'wp_footer',
static function (): void {
$config = array(
'ga' => 'G-XXXXXXXXXX', // Your GA4 measurement ID, or '' for none.
'pixel' => '000000000000000', // Your Meta Pixel ID, or '' for none.
);
$js = <<<'JS'
( function ( cfg ) {
var done = {};
function loadGa() {
if ( done.ga || ! cfg.ga ) { return; }
done.ga = true;
window.dataLayer = window.dataLayer || [];
window.gtag = window.gtag || function () { window.dataLayer.push( arguments ); };
window.gtag( 'js', new Date() );
window.gtag( 'config', cfg.ga );
var s = document.createElement( 'script' );
s.async = true;
s.src = 'https://www.googletagmanager.com/gtag/js?id=' + encodeURIComponent( cfg.ga );
document.head.appendChild( s );
}
function loadPixel() {
if ( done.pixel || ! cfg.pixel || window.fbq ) { return; }
done.pixel = true;
var n = window.fbq = function () {
n.callMethod ? n.callMethod.apply( n, arguments ) : n.queue.push( arguments );
};
window._fbq = window._fbq || n;
n.push = n; n.loaded = true; n.version = '2.0'; n.queue = [];
var s = document.createElement( 'script' );
s.async = true;
s.src = 'https://connect.facebook.net/en_US/fbevents.js';
document.head.appendChild( s );
window.fbq( 'init', cfg.pixel );
window.fbq( 'track', 'PageView' );
}
function check() {
if ( typeof window.wp_has_consent !== 'function' ) { return; } // No consent API: load nothing.
if ( window.wp_has_consent( 'statistics' ) ) { loadGa(); }
if ( window.wp_has_consent( 'marketing' ) ) { loadPixel(); }
}
// On page load (returning visitors), and the moment consent changes (no reload).
document.addEventListener( 'DOMContentLoaded', check );
document.addEventListener( 'wp_listen_for_consent_change', check );
} )( CONFIG );
JS;
wp_print_inline_script_tag(
str_replace( 'CONFIG', (string) wp_json_encode( $config, JSON_HEX_TAG | JSON_HEX_AMP ), $js )
);
},
100
);
A few things worth knowing about it:
- It fails closed. If the WP Consent API isn’t loaded, nothing is tracked. That’s the safe default for EU sites. If you see no data at all, check this first.
- It loads each tag once, however many times consent changes, and it leaves the Pixel alone if another plugin already loaded one.
- It works with page caching. Every visitor gets the same HTML, and the decision happens in the browser. A PHP check like
if ( wp_has_consent( 'statistics' ) )around the tag would break here: the first visitor’s choice would be cached and served to everyone. - The IDs are printed with
wp_json_encode(), andwp_print_inline_script_tag()lets security plugins add a CSP nonce.
One limit: if a visitor withdraws consent, scripts that already ran stay in memory until the next page. Google and Meta both have consent commands (gtag( 'consent', 'update', … ) and fbq( 'consent', 'revoke' )) if you need to handle that on the same page.
How to test it (two minutes)
- Open the site in a private window, open DevTools → Network, and type
collect|facebookinto the filter (tick the regex option, or filter one word at a time). - Before you choose: no requests to
google-analytics.com,googletagmanager.comorfacebook.com. - Click Reject, browse two pages: still nothing.
- In a new private window, click Accept: the requests appear right away, on the same page.
- Reload: they load straight away, with no banner.
If GA appears before you’ve chosen anything, there’s a second copy of the tag somewhere. View the page source and search for your G- ID.
Where Snipfire helps
In Snipfire, the plugin I make, this is a setting instead of a snippet. Paste the Google Analytics 4 or Meta Pixel snippet from the free library, and with Snipfire Pro set Cookie consent to “Wait for: Statistics” or “Marketing”. The snippet stays inert until the visitor accepts that category, then runs on the spot, with no reload. It works with Complianz, CookieYes, Cookiebot, Borlabs Cookie, the WP Consent API and Google Consent Mode v2, with page caching, and it respects Global Privacy Control for marketing scripts.
But you don’t need it: Option 3 above does the job with any banner that supports the WP Consent API.
The short version
- Load GA and the Pixel in one place only: duplicates are why tags ignore Reject.
- Let your banner load them, or mark them with its attribute.
- For a banner-independent setup, use the WP Consent API and the snippet above.
- Test in a private window with the Network tab, before and after Accept.
Using a banner I didn’t mention, or seeing something odd in your tests? Tell me and I’ll take a look.
Antony, maker of Snipfire