Send the visitor choice from TermsFeed Free Cookie Consent to Google tags with Consent Mode v2
Set every Google Consent Mode v2 signal to denied in the first script in <head>, after only the charset meta, then update the signals from the user_consent_saved callback of Cookie Consent. Google requires advertisers to send consent signals for traffic from the European Economic Area (EEA), according to the Google Ads Help article "Updates to consent mode for traffic in EEA".
Cookie Consent does not send any Consent Mode signal of its own, so your callback code sends every signal. The setup takes about 20 minutes.
What Google Consent Mode v2 is and how your Cookie Consent callback sends it
Google Consent Mode v2 is a Google API that tells Google tags whether to store cookies and use data for ads, through 4 signals. The signals are ad_storage, ad_user_data, ad_personalization, and analytics_storage. Each one holds granted or denied, and Google tags adjust what they store and send to match.
Google names 2 ways to set up a Google tag with a consent banner. Tagging the Google tag with type="text/plain" keeps it from loading until the visitor accepts, which Google calls basic consent mode. Loading the Google tag untagged with denied defaults lets it send cookieless pings before the answer. Google calls that setup advanced consent mode, according to the Google Analytics Help article "About consent mode". Set up advanced consent mode, with an untagged Google tag, for cookieless pings before the answer. For no request before consent, tag the Google tag with Block Google Analytics 4 Until Consent.
In the fixed category mapping, the tracking category sets analytics_storage, the targeting category sets ad_storage, ad_user_data, and ad_personalization, and the functionality category sets functionality_storage and personalization_storage. Check every vendor signal each category controls in Map Categories to Vendor Consent Signals.
The setup needs the Google tag ID of your Google Analytics 4 property or Google Ads account, which is G- or AW- followed by letters and digits. Your pages must already hold the Cookie Consent code from the TermsFeed generator. Verifying the setup needs Google Chrome and access to Google Tag Assistant at tagassistant.google.com.
Step 1: Set every consent signal to denied before any Google tag
Paste the Consent Mode default as the first script in <head>, after only the charset meta. The default goes above the Google tag, the Google Tag Manager container, and the Cookie Consent block. Google tags read the consent state when they load. A default that runs after a Google tag comes too late, and that tag runs with no consent state.
Copy the default script, which sets the 4 Consent Mode v2 signals and the 2 other storage signals, functionality_storage and personalization_storage, to denied.
<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',
'functionality_storage': 'denied',
'personalization_storage': 'denied',
'wait_for_update': 500
});
</script>
The wait_for_update value holds the first hit of Google tags for up to 500 milliseconds (half a second) while your callback sends the update.
Keep the default untagged and outside the Cookie Consent block. A default inside a tagged script runs only after consent, which leaves Google tags with no default.
Step 2: Load the Google tag without a Cookie Consent tag
Paste the Google tag directly below the default script, as Google gives it, with no type="text/plain" and no data-cookie-consent attribute. The tag loads on every page and follows the consent signals. The denied default above it keeps it from storing cookies until the visitor accepts.
Replace G-XXXXXXXXXX in this Google tag for a Google Analytics 4 property with your own tag ID, in both places.
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
</script>
Keep a single copy of the Google tag on the page. A second, tagged copy of the same tag runs again on accept and sends every hit twice.
Step 3: Send the consent state from user_consent_saved
Add a user_consent_saved callback to the run() options, and set "user_consent_saved_on_page_load": true beside it. The callback receives the saved categories in details and sends granted or denied for the signals of each category. Google does not remember an update from one page to the next, so the callback sends the saved choice again on every page load.
Cookie Consent calls user_consent_saved before it releases any tagged script, so a tagged script starts with the update in place. The untagged Google tag is not held, so it often loads first and usually receives the update within the wait_for_update window. Without user_consent_saved_on_page_load, the callback skips every later page load of a visitor who answered, and Google tags on those pages keep the denied default. Read every moment the callback runs in Callbacks and DOM Events.
Your Cookie Consent block looks like this with the callback in place. The TermsFeed generator writes the run() options that come before user_consent_saved_on_page_load, so keep your own values for them.
<!-- Cookie Consent by TermsFeed https://www.TermsFeed.com -->
<script type="text/javascript" src="https://www.termsfeed.com/public/cookie-consent/4.2.0/cookie-consent.js" charset="UTF-8"></script>
<script type="text/javascript" charset="UTF-8">
document.addEventListener('DOMContentLoaded', function () {
cookieconsent.run({
"notice_banner_type": "headline",
"consent_type": "express",
"palette": "dark",
"language": "en",
"page_load_consent_levels": ["strictly-necessary"],
"notice_banner_reject_button_hide": false,
"preferences_center_close_button_hide": false,
"page_refresh_confirmation_buttons": false,
"website_name": "Example",
"user_consent_saved_on_page_load": true,
"callbacks": {
"user_consent_saved": (details) => {
gtag('consent', 'update', {
'analytics_storage': details.tracking ? 'granted' : 'denied',
'ad_storage': details.targeting ? 'granted' : 'denied',
'ad_user_data': details.targeting ? 'granted' : 'denied',
'ad_personalization': details.targeting ? 'granted' : 'denied',
'functionality_storage': details.functionality ? 'granted' : 'denied',
'personalization_storage': details.functionality ? 'granted' : 'denied'
});
}
}
});
});
</script>
<noscript>Free cookie consent management tool by <a href="https://www.termsfeed.com/">TermsFeed Generator</a></noscript>
<!-- End Cookie Consent by TermsFeed https://www.TermsFeed.com -->
The callback sends denied for every category the visitor refused, so a click on the "I decline" button keeps every signal denied. A category left out of notice_banner_purposes_levels reads false in details, so its signals stay denied.
The callback sends denied for a category the visitor withdraws as soon as the "Save my preferences" button saves the change. Google tags then stop storing new cookies for that category. The older setup that grants signals from scripts_specific_loaded with callbacks_force: true still works, but it does not send a denied update, so the default set in the page applies from the next page load.
The complete page with Google Consent Mode v2 and Cookie Consent
In a working page, the Cookie Consent block sits before the closing </body> tag. The link with the id open_preferences_center lets visitors change their choice later.
Replace G-XXXXXXXXXX in the full page with your tag ID and Example with the name of your site.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example page</title>
<!-- 1. Google Consent Mode v2 defaults, the first script after the charset meta -->
<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',
'functionality_storage': 'denied',
'personalization_storage': 'denied',
'wait_for_update': 500
});
</script>
<!-- 2. Google tag, untagged -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
</script>
</head>
<body>
<main>
<h1>Example page</h1>
</main>
<footer>
<a href="#" id="open_preferences_center">Update cookies preferences</a>
</footer>
<!-- 3. Cookie Consent by TermsFeed https://www.TermsFeed.com -->
<script type="text/javascript" src="https://www.termsfeed.com/public/cookie-consent/4.2.0/cookie-consent.js" charset="UTF-8"></script>
<script type="text/javascript" charset="UTF-8">
document.addEventListener('DOMContentLoaded', function () {
cookieconsent.run({
"notice_banner_type": "headline",
"consent_type": "express",
"palette": "dark",
"language": "en",
"page_load_consent_levels": ["strictly-necessary"],
"notice_banner_reject_button_hide": false,
"preferences_center_close_button_hide": false,
"page_refresh_confirmation_buttons": false,
"website_name": "Example",
"user_consent_saved_on_page_load": true,
"callbacks": {
"user_consent_saved": (details) => {
gtag('consent', 'update', {
'analytics_storage': details.tracking ? 'granted' : 'denied',
'ad_storage': details.targeting ? 'granted' : 'denied',
'ad_user_data': details.targeting ? 'granted' : 'denied',
'ad_personalization': details.targeting ? 'granted' : 'denied',
'functionality_storage': details.functionality ? 'granted' : 'denied',
'personalization_storage': details.functionality ? 'granted' : 'denied'
});
}
}
});
});
</script>
<noscript>Free cookie consent management tool by <a href="https://www.termsfeed.com/">TermsFeed Generator</a></noscript>
<!-- End Cookie Consent by TermsFeed https://www.TermsFeed.com -->
</body>
</html>
Step 4: Verify Consent Mode in Google Tag Assistant
The checks assume express consent with page_load_consent_levels left out or at the generator value ["strictly-necessary"], and the "I decline" button shown. Check the consent state of your Google tag in Google Tag Assistant, before and after the answer. Tag Assistant shows the default and each update in the "Consent" tab of the tag, according to the Google Tag Platform guide Troubleshoot consent mode with Tag Assistant.
Publish the changes and clear any page cache. Close every private window, then run these checks in a new one, so no earlier answer hides the consent banner.
- Go to
tagassistant.google.com, click the "Add domain" button, enter your page address, and click the "Connect" button. Your site opens in a new window with the consent banner showing, or in a new tab when you allowed the Tag Assistant extension in private windows. - In the Tag Assistant Summary, select the earliest "Consent" event. Open your Google tag, select the "Consent" tab in its Output, and confirm that the "On-page Default" column shows "Denied" for
ad_storage,ad_user_data,ad_personalization, andanalytics_storage. - Click the "I agree" button on the site Tag Assistant opened. Select the most recent "Consent" event and confirm that the "On-page Update" column shows "Granted" for
analytics_storage, and for the 3 ad signals when thetargetingcategory is in use. - Open another page of your site in that window or tab, and confirm that the "On-page Update" column of the most recent "Consent" event shows "Granted" again. That result confirms that
user_consent_saved_on_page_loadsends the saved choice on every page. - With a Google Analytics 4 tag, open Chrome DevTools in that window or tab, select the Network tab, type
collectin the filter, and reload. With thetargetingcategory in use, requests togoogle-analytics.com/g/collectcarrygcs=G111, which marks both storage signals as granted. Withouttargeting, they carrygcs=G101. - Close every private window, open a new one, connect Tag Assistant again, click the "I decline" button, and select the "Consent" event that appears after the click. Confirm that the "On-page Update" column shows "Denied" for the 4 signals and that the Application tab of Chrome DevTools does not show a
_gacookie under Cookies.
Common mistakes with Google Consent Mode v2
Find each mistake from its symptom in Tag Assistant or the Network tab, then fix it in your page source or the run() options.
- The default runs after the Google tag. Tag Assistant reports that a tag read the consent state before a default was set. Move the default script above every Google script, as the first script in
<head>, after only the charset meta. user_consent_saved_on_page_loadis missing. Tag Assistant shows "Granted" on the page where the visitor answered and "Denied" on the next page. Add"user_consent_saved_on_page_load": trueto therun()options.- The Google tag is still tagged when you want advanced consent mode. With
collecttyped in the Network filter before you answer, no request appears, so Google does not receive a cookieless ping, and it models conversions only from a general model. Search your page source forgtag/js, and removetype="text/plain"anddata-cookie-consentfrom the tag. A tagged Google tag is basic consent mode, which still takes theuser_consent_savedupdate. - A signal name is misspelled. Google ignores a signal it does not know, such as
ads_storageoranalytics-storage, and the real signal keeps its denied default. Tag Assistant lists the misspelled name in the API Call of the update Consent event. Copy the names from the denied default script. - The callback is the name of a
letorconstfunction. Cookie Consent finds a callback named by a string only onwindow, so alet,const, or module function never runs. Tag Assistant then does not show an "On-page Update" after the visitor answers. Pass the function itself, as in"user_consent_saved": function (details) { … }. - A region default is set to granted. Google tags in that region run with granted consent until the callback sends its first update after
DOMContentLoaded, before the visitor answers. Cookie Consent shows the same consent banner in every country. Search your page source for agtag('consent', 'default'call that carries aregionlist, and keep every default denied.
A denied update does not delete cookies a Google tag stored earlier, and Cookie Consent does not delete cookies set by a Google tag either. A _ga cookie set before the visitor withdrew stays until it expires, unless your own code removes it. Copy the cleanup callback from Rejecting Does Not Delete Cookies Already Set.
A "Denied" state in Tag Assistant that stays after you fix every common mistake has another cause. Follow each cause and its fix in Google Consent Mode Stays Denied.
Add Google Ads, Microsoft Clarity, Meta, TikTok, and HubSpot signals beside Consent Mode
Google Ads conversion tracking reads the same 3 ad signals, so your user_consent_saved callback covers it with no change. Add the conversion event with Google Ads Conversion Tracking.
Microsoft Clarity, the Meta Pixel, the TikTok Pixel, and HubSpot take their own consent commands, which their recipes grant from scripts_specific_loaded with callbacks_force: true. That callback runs beside your user_consent_saved callback and does not send any Google signal. Copy both callbacks together from Map Categories to Vendor Consent Signals.