GA4 and AdSense: The Ultimate Guide to Verifying Consent Mode V2 (G111)
Since Google introduced Consent Mode V2 (GCM V2) and made the two new parameters (ad_user_data and ad_personalization) mandatory for EEA/UK traffic, many website owners have seen a sharp drop in their AdSense revenues.
The reason is simple: if Google doesn’t receive the required consent signal, it defaults to serving non-personalized ads, which have a significantly lower Cost Per Mille (CPM) and Cost Per Click (CPC).
The key to maximizing your revenue is proving that the signal is correctly sent and received.
This guide provides the ultimate, two-step verification process to ensure your website, whether built on WordPress, Shopify, a custom solution, or any other CMS, is successfully sending the correct GCS=G111 signal, which is the green light for high-value personalized ads.
1. Why Consent Mode V2 Is Critical for Your AdSense Revenue
The Core Problem: Personalized vs. Non-Personalized Ads
Google relies on tracking signals to serve highly targeted ads. Targeted ads command higher bids from advertisers.
- When Consent is GRANTED (GCS=G111): AdSense serves personalized ads. Revenue is maximized.
- When Consent is DENIED (GCS=G100): AdSense serves non-personalized ads (or uses basic modeling). Revenue drops significantly (often 30-70%).
Your GA4 implementation, typically done via gtag.js or Google Tag Manager, must properly communicate the user’s choice to Google’s ad systems.
The Importance of the GCS Parameter
The Google Consent Status (GCS) parameter is the single source of truth. It is sent within every relevant GA4 and AdSense network request.
| GCS Value | Meaning | Revenue Impact |
| G111 | All storage/data granted (ad_storage and analytics_storage are 1). | Maximum Revenue (Personalized Ads) |
| G100 | All storage/data denied (ad_storage and analytics_storage are 0). | Minimum Revenue (Non-Personalized Ads) |
If you see G100 after a user accepts consent, your integration is failing.
2. The Two-Step Verification Process (Platform Agnostic)
A common failure point is a false positive, your Cookie Management Platform (CMP) thinks it sent the update, but Google never received it. You need two complementary scripts to confirm both the Intention (what you sent) and the Reality (what Google received).
The following scripts rely only on the browser’s developer tools and the standard Google Tag data layer (dataLayer), making them work across any web platform.
Step 1: Verify the Intention (The dataLayer Check)
This script checks the dataLayer to ensure your CMP sends the correct gtag(‘consent’, ‘update’, …) command with the required parameters.
Procedure
- Open your website in an Incognito/Private window.
- Open the browser Console (F12).
- Copy and paste the script below into the Console and press Enter.
The dataLayer Status Checker Script
(function() {
console.clear();
console.log("%c🕵️ INTENTION CHECK: dataLayer Consent Commands", "background: #202124; color: #fff; font-size: 14px; padding: 10px; border-radius: 5px; font-weight: bold;");
const consentCommands = (dataLayer || []).filter(entry => entry && (entry[0] === 'consent'));
if (consentCommands.length === 0) {
console.warn("⚠️ No consent commands found in dataLayer.");
return;
}
console.log(`%cFound ${consentCommands.length} consent event(s):`, "color: #1a73e8; font-weight: bold;");
consentCommands.forEach((cmd, index) => {
const type = cmd[1].toUpperCase();
const params = cmd[2];
const styles = type === 'DEFAULT'
? "background: #ffe0b2; color: #e65100; font-weight: bold; padding: 2px 5px; border-radius: 3px;"
: "background: #c8e6c9; color: #2e7d32; font-weight: bold; padding: 2px 5px; border-radius: 3px;";
console.groupCollapsed(`%c${index + 1}. ${type}`, styles);
const statusTable = {};
const criticalKeys = ['ad_storage', 'ad_user_data', 'ad_personalization', 'analytics_storage'];
criticalKeys.forEach(key => {
if (params[key]) {
statusTable[key] = params[key];
}
});
console.table(statusTable);
if (params['ad_personalization'] === 'granted' && params['ad_user_data'] === 'granted') {
console.log("%c✅ AdSense Personalized Data Signal: GRANTED", "color: green; font-weight: bold;");
} else {
console.log("%c⛔ AdSense Personalized Data Signal: DENIED or LIMITED", "color: red; font-weight: bold;");
}
console.groupEnd();
});
})();
Expected Results for Step 1
| State | Expected Console Output | Meaning |
| Initial Load | 1. DEFAULT with denied for storage/data parameters. | Your site is legally compliant (GCM V2 starts restricted). |
| After Accepting | A new line X. UPDATE with granted for storage/data parameters. | Your CMP is sending the correct signal (Intention verified). |
Step 2: Verify the Reality (The Network Check)
This script is the “single source of truth.” It monitors the actual network requests to Google to see if the gcs parameter confirms the status change.
Procedure
- Keep the Console open and refresh the page.
- Paste the script below into the Console and press Enter.
- Accept the cookies on your CMP banner.
- Navigate to a different page on your site to force a new GA4/AdSense request.
The Network Monitor Script
(function() {
console.clear();
console.log("%c📡 REALITY CHECK: Network GCS Monitoring", "background: #000080; color: #fff; font-size: 14px; padding: 10px; border-radius: 5px; font-weight: bold;");
console.log("Monitoring Google Analytics / Ads network requests for GCS status...");
function decodeGCS(gcs) {
if (!gcs) return "UNKNOWN (Parameter missing)";
if (gcs === 'G100') return '❌ G100 : All Denied (Anonymous/Basic)';
if (gcs === 'G111') return '✅ G111 : FULLY GRANTED (Ads + Analytics)';
return `❓ ${gcs} : Partial or complex status`;
}
const observer = new PerformanceObserver((list) => {
list.getEntriesByType('resource').forEach((entry) => {
if (entry.name.includes('/collect') || entry.name.includes('google-analytics') || entry.name.includes('doubleclick')) {
const urlObj = new URL(entry.name);
const gcs = urlObj.searchParams.get('gcs');
if (gcs) {
const isGranted = gcs === 'G111';
const style = isGranted
? "background: #e8f5e9; color: #1b5e20; border-left: 4px solid #4caf50; padding: 4px;"
: "background: #ffebee; color: #b71c1c; border-left: 4px solid #f44336; padding: 4px;";
console.groupCollapsed(`%c📡 Hit Detected: ${gcs}`, style.replace('padding: 4px;', 'font-weight:bold;'));
console.log(`%cStatus: ${decodeGCS(gcs)}`, "font-weight:bold;");
console.log("URL:", entry.name);
console.groupEnd();
}
}
});
});
observer.observe({ type: 'resource', buffered: true });
})();
Expected Results for Step 2
| Action | Expected Console Output | Revenue Status |
| Initial Load | Hits show G100 | Legal compliance is met. |
| After Accepting & Navigation | New hits show G111 | SUCCESS! Personalized ads are enabled. Revenue is optimized. |
| Failure Scenario | Hits remain at G100 | FAILURE. The GCM V2 update is not being processed correctly. AdSense revenue remains low. |
3. Troubleshooting: Why You Might Still See G100
If Step 2 fails (you remain at G100 after accepting), the communication bridge between your CMP and Google is broken.
- CMP Loading Conflict: Your CMP script is loading after the main GA4 code, meaning the consent update is missed by Google. Fix: Ensure your CMP is loaded first and can execute the gtag(‘consent’, ‘default’, …) command.
- Missing Parameters: Your CMP is not updated to include the mandatory ad_user_data and ad_personalization parameters. Fix: Update your CMP solution to the latest GCM V2 compatible version.
- Caching Issues: Server-side or CMS caching (e.g., WordPress) is serving stale code. Fix: Clear all caches and try the test in an Incognito window again.
By verifying both the Intention in the dataLayer and the Reality in the Network status, you gain full control over your GCM V2 implementation and protect your AdSense earnings.
Your comments enrich our articles, so don’t hesitate to share your thoughts! Sharing on social media helps us a lot. Thank you for your support!
