You installed a cookie consent banner and something else stopped working. YouTube and map embeds show a gray box or a “please accept cookies” placeholder. The contact form says “reCAPTCHA verification failed” or the submit button does nothing. Google Analytics traffic fell off a cliff the day the banner went live. Or the banner itself slides in late and shoves the page down, and PageSpeed Insights now blames it for your layout shift.
None of this means the banner is faulty. A consent tool works by stopping scripts from running until a visitor says yes, and it can only guess which scripts belong in which category. When it guesses wrong, or when a cache or speed plugin interferes with that guessing, the wrong things get blocked. This guide is for site owners and designers who need the banner to stay and the site to work. It is a technical guide, not legal advice: what you must block and when is a question for a qualified adviser.
What is happening
A consent management platform (CMP) is the plugin or service that shows the banner, stores the visitor’s choice and controls which scripts run. Most use one or more of these methods.
| Method |
How it works |
What breaks when it goes wrong |
| Script type rewriting |
The CMP changes <script type="text/javascript"> to <script type="text/plain">. Browsers do not execute text/plain. After consent, the CMP swaps the type back and re-inserts the script. |
A script needed by a form or slider is never re-inserted, or is re-inserted after code that depends on it has already run and failed. |
| Category attributes |
Scripts and iframes are tagged with an attribute such as data-category="analytics" or data-cookieconsent="statistics". Iframes may have src moved to data-src so nothing loads. |
Embeds stay blank. A whole category is blocked because one script in it was mis-filed. |
| Auto-blocking by domain |
The CMP scans outgoing script and iframe URLs and blocks known tracking domains before they load. |
Anything served from a google.com domain, including reCAPTCHA and Maps, can be caught along with advertising tags. |
| Tag manager consent checks |
Google Tag Manager reads the consent state and only fires tags whose required consent is granted. |
Tags fire with the wrong state, or never fire, because the default was set too late. |
| Consent signals (Google Consent Mode) |
Google tags always load but adjust their behavior based on ad_storage, analytics_storage, ad_user_data and ad_personalization. |
Analytics drops sharply when tags are both blocked by the CMP and set to denied, so nothing at all reaches Google. |
The symptoms you see map closely to which method is misfiring.
- Blank embeds: the iframe is in a blocked category and the CMP shows a placeholder until consent. Expected behavior, unless the placeholder never clears after the visitor accepts.
- reCAPTCHA failing: the reCAPTCHA script is blocked, so the form submits without a token and the server rejects it.
- Analytics near zero: GA4 only receives data from visitors who accept, or receives nothing because the script is blocked even after acceptance.
- Layout shift: the banner is inserted into the normal page flow at the top or bottom instead of floating over it.
Before you start
You need admin access to WordPress, access to the CMP settings (plugin or the CMP’s own dashboard), and ideally access to Google Tag Manager and Google Analytics. Take a backup or at least export the CMP settings before you change them.
Most work here happens in the browser. Use Chrome or Edge DevTools: press F12 (or Cmd+Option+I on a Mac), then use the Console, Network and Application tabs. A few checks use curl on your own computer: macOS and Linux include it, and Windows 10 and 11 include curl.exe in PowerShell. If you have SSH access, the WP-CLI commands run in the site folder, the one containing wp-config.php; your host may name the command wp or wp-cli.
Always test in a private window. Your normal browser already holds a consent cookie, so it never shows you what a first-time visitor sees.
Step 1: Confirm the banner is the cause
Do this first, because a form or analytics problem that started the same week can have nothing to do with consent.
Accept everything and retest
Open a private window, load the broken page, click “Accept all” and reload. If the embed, form or analytics hit now works, consent blocking is the cause and you need to recategorize something. If it still fails after accepting, either the CMP is not unblocking properly (Step 3 and Step 5) or the problem lies elsewhere.
Look for neutered scripts in the HTML
On your own computer, fetch the raw page and list scripts the CMP has rewritten.
curl -s https://example.com/PAGE_PATH/ | grep -o '<script[^>]*text/plain[^>]*>'
Replace example.com/PAGE_PATH/ with the broken page’s address. -s hides the progress meter; grep -o prints only the matching tag. Good output for a page with only analytics blocked is a short list of tags pointing at analytics or advertising domains. Bad output is a tag whose src points at recaptcha, a form plugin, a slider library or your theme: that script is being held back as if it were tracking. No output at all means the CMP blocks in the browser after load rather than in the HTML, so use the Network tab instead.
Watch the Network tab
In DevTools, open Network, check “Disable cache”, reload, and filter by the service name (for example recaptcha, youtube or maps). A request that is missing before consent and appears after you click Accept confirms the CMP is gating it. A request that shows as “(blocked:other)” or never appears even after consent suggests a blocker, a speed plugin, or a CMP that fails to release it.
Step 2: Fix scripts in the wrong category
This is the most common cause. Automatic scanners classify by domain and cookie name, and they get things wrong.
reCAPTCHA and other security scripts
If reCAPTCHA sits in a “Marketing” or “Statistics” category, visitors who decline cannot submit your forms. Most CMPs let you move a service to “Necessary” or “Functional”, or exclude a script URL from blocking. Whether a security script can lawfully run without consent is a legal judgment, and opinions differ, so make that call with your adviser. A practical alternative is a challenge that does not depend on Google, such as Cloudflare Turnstile or a honeypot, which removes the conflict.
Embeds and maps
For YouTube, Vimeo and Google Maps, the right behavior under a strict setup is a placeholder with a “load this video” button that grants consent for that service. Check that your CMP offers this. If the placeholder stays blank after the visitor accepts, the CMP probably moved src to data-src but your page builder lazy-loads the iframe with its own script that never sees the restored attribute. Test by disabling the builder’s lazy load for that one embed.
Scripts your theme or builder needs
If a slider, accordion or menu stops working, look in the CMP’s script or cookie list for anything from your own domain or from a library CDN. Your own first-party scripts rarely need consent. Move them out of the blocked category or add them to the CMP’s exclusion list, then retest in a private window without accepting anything.
Step 3: Fix Google Consent Mode v2 setup
Consent Mode is Google’s way for its tags to respect a visitor’s choice without being removed from the page. Version 2 added two signals for advertising. Google documents it at developers.google.com, and you should check your setup against that page, because details change.
| Signal |
Controls |
ad_storage |
Cookies and storage for advertising |
analytics_storage |
Cookies and storage for analytics, such as GA4’s client ID |
ad_user_data |
Whether user data may be sent to Google for advertising |
ad_personalization |
Whether data may be used for personalized advertising such as remarketing |
The correct order
A default state must be set before any Google tag loads, then updated when the visitor chooses. In plain gtag.js that looks like this, placed in the page head above the Google tag.
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('consent', 'default', {
'ad_storage': 'denied',
'ad_user_data': 'denied',
'ad_personalization': 'denied',
'analytics_storage': 'denied',
'wait_for_update': 500
});
</script>
This is a snippet in your theme head or a header code setting, but most CMPs output it for you, which is preferable. 'default' sets the starting state; each signal is 'granted' or 'denied'. wait_for_update is the number of milliseconds tags wait for the CMP to report a stored choice before firing with the default. There are no placeholders here. Good: the CMP later calls gtag('consent', 'update', {...}) with 'granted' values after acceptance. Bad: the default appears below the Google tag in the source, or appears twice with different values because two plugins both output it.
Basic versus advanced mode
In basic mode the CMP blocks Google tags completely until consent, so non-consenting visitors send nothing. In advanced mode tags load with denied defaults and send cookieless pings that Google can use for modeling. Which one you may use is a legal question. The technical trap is doing both by accident: the CMP blocks gtag.js entirely and also expects consent mode to unblock it. Pick one approach and configure the CMP and your analytics plugin to match.
Google Tag Manager
In GTM, the CMP’s template should fire on the “Consent Initialization - All Pages” trigger, which runs before every other trigger. Turn on the consent overview under Admin, Container Settings, “Enable consent overview”, then check that each tag lists the consent types it needs. A GA4 tag that fires on “Initialization” or “All Pages” before the CMP sets its default is the classic cause of tags firing with no consent state at all.
Step 4: Test each consent state in the browser
You should be able to prove what Google receives in each state, rather than guessing from reports a day later.
Read the gcs parameter
In a private window, open DevTools Network and filter for collect. Reload. GA4 hits go to a URL containing /g/collect. Click one and look at the query string parameters for gcs.
| gcs value |
Meaning |
G100 |
ad_storage denied, analytics_storage denied |
G101 |
ad_storage denied, analytics_storage granted |
G110 |
ad_storage granted, analytics_storage denied |
G111 |
Both granted |
The format is G1 followed by one digit for ad storage and one for analytics storage. Newer hits also carry a longer gcd parameter that encodes all four v2 signals and whether each came from a default or an update. Good: G100 before you click anything (in advanced mode), then G111 on the next hit after you accept. Bad: no gcs at all, which means consent mode is not configured, or G111 before you have clicked anything, which means defaults are missing or set to granted.
Read the consent commands in the console
In the Console tab, run this to list every consent command pushed so far.
window.dataLayer.filter(function (e) { return e && e[0] === 'consent'; })
This runs in the browser console on your live page; it has no placeholders. Each result is an arguments list such as ['consent', 'default', {...}]. Good: one default entry first, then an update entry after you accept. Bad: an empty array (the CMP is not using gtag consent commands, or uses GTM’s template API, so check Tag Assistant instead) or two default entries with different values.
Use Tag Assistant
Google’s Tag Assistant (tagassistant.google.com) connects to your site and shows a Consent tab for each event, listing the on-page default and update for every signal. It is the clearest view when GTM is involved.
Clear your choice between tests
Your choice lives in a cookie or local storage entry. In DevTools, open Application, then Storage, and click “Clear site data”, or simply close the private window and open a new one. Test three paths: accept all, reject all, and customize with only analytics allowed.
Step 5: Check caching and speed plugins
Page caching and consent banners can conflict in two ways.
Cached consent state
Some CMPs decide on the server which scripts to output based on the visitor’s consent cookie. A full-page cache stores the first version it sees and serves it to everyone. If the first visitor had accepted, later visitors get unblocked scripts before consenting; if they had declined, people who accept still see blocked scripts until the cache clears. Check from your own computer.
curl -sI https://example.com/ | grep -i -E 'cache|age|cf-cache-status|vary'
-I requests only the headers; grep -i -E matches any of the listed header names regardless of case. Replace example.com with your domain. A header such as x-cache: HIT, cf-cache-status: HIT or a non-zero age means the page came from a cache. That is fine if your CMP decides everything in JavaScript in the browser. If the CMP renders consent server-side, it must either be switched to client-side mode or your cache must be configured to vary on, or bypass for, the consent cookie. Your CMP’s documentation lists its cookie name.
Delayed, deferred and combined JavaScript
Speed plugins that delay JavaScript until user interaction, combine files or defer everything can make the banner appear late, cause it to miss its window to set defaults, or stop it from re-inserting scripts. Exclude the CMP’s script from delay, defer, combine and minify settings, and if you use Cloudflare, check that Rocket Loader is not rewriting it. JavaScript broken by speed plugins in general is covered in the separate guide on that topic.
After any change, purge every cache layer and retest in a private window.
Step 6: Stop the banner hurting LCP and CLS
The banner is real content as far as Core Web Vitals are concerned.
Layout shift
A banner inserted at the top of the page, above your header, pushes everything down when it appears. That counts as layout shift. The fix is to make it overlay the page with fixed positioning rather than sitting in the normal flow; most CMPs offer a “floating” or “bottom overlay” layout. To confirm, open DevTools, press Ctrl+Shift+P (Cmd+Shift+P on a Mac), run “Show Rendering”, check “Layout Shift Regions” and reload. If the whole page flashes blue when the banner appears, the banner is causing the shift.
Largest Contentful Paint
On a phone, a banner with a paragraph of text can be the largest element on screen, so its render time becomes your LCP. A banner script loaded from a third-party domain, then fetching its configuration, then rendering, can add noticeable delay. In the DevTools Performance panel, record a page load and look at the LCP marker: if it highlights the banner text, shorten the text, make sure the banner script loads early (not delayed), and prefer a CMP that can serve its script and configuration from your own domain or a fast CDN. The general approach to LCP is covered in the separate guide on slow sites caused by design choices.
Advanced: tracing one script from blocked to running
When a single script refuses to unblock, trace it through its whole life on the page.
- In a private window with DevTools open, use the Elements panel search (Ctrl+F inside Elements) for the script’s file name. Note its
type and any data- attributes before consent.
- In Sources, open Event Listener Breakpoints and set one on DOM Mutation (or right-click the script’s parent in Elements and choose “Break on”, “Subtree modifications”).
- Click Accept. The debugger pauses when the CMP modifies the DOM. Step through and check whether it creates a new script element with
type restored, or only changes the attribute on the existing one. Changing the attribute on an existing element does not make browsers execute it; a working CMP inserts a fresh element.
- Check the Console for errors right after acceptance. A
ReferenceError such as “grecaptcha is not defined” means code that depends on the script ran before the script was restored. The dependent code must also be in the same category, or wait for the CMP’s “consent given” event.
Most CMPs fire a JavaScript event or callback after consent changes. If you have custom code, wrap it so it runs on that event rather than on page load. On WordPress, the WP Consent API plugin offers a shared way for plugins to read consent categories; check whether your CMP and analytics plugin both support it so they agree on state.
For a quick sanity check of what a first-time visitor triggers, run a Lighthouse or PageSpeed Insights test: it loads the page with no stored cookies, so you see the banner’s real cost. The diagnostic tools page links tools that show headers and third-party requests.
When to contact your host
Most consent problems live in the CMP and tag configuration, not the server. Open a ticket with your host when you suspect server or CDN caching is serving the wrong consent state, or when you cannot exclude a cookie from the page cache yourself. Send:
- the affected URLs and the times you tested, in UTC;
- the response headers from the curl command above, including any cache status and
age;
- the CMP’s consent cookie name and a request to bypass or vary the page cache on it;
- what changed and when (banner installed, speed plugin enabled, CDN rule added).
If the site is fully broken rather than partly broken, start at the Emergency Room first.