One of the most common issues after implementing Google Consent Mode is losing the original source/medium in Google Analytics 4 (GA4). Instead of attributing sessions to the correct acquisition channel, GA4 often records them as Direct after users grant consent.
This happens because Consent Mode changes how GA4 initializes tracking before analytics cookies are accepted.
In this article, I’ll explain why this happens and show a simple solution for preserving the original traffic source using first-party cookies and Google Tag Manager (GTM).
Why Source/Medium Gets Lost in GA4 with Consent Mode
Without Google Consent Mode
When using a traditional cookie banner without Google Consent Mode, tracking begins only after the visitor grants consent.
Once analytics cookies are accepted, the GA4 Configuration tag fires for the first time, a session is created, and the initial page_view event is sent together with all acquisition information, including the original source and medium.
From that point on, GA4 associates every subsequent event with the same session and preserves the original attribution while the visitor navigates through your website.
The process looks like this:
- Visitor lands on the website.
- Analytics cookies are not available yet.
- Visitor accepts cookies.
- GA4 initializes a new session.
- The first page_view event is sent.
- GA4 stores the original source/medium.
- The visitor continues browsing the website.
- All subsequent events remain associated with the original acquisition source.
With Google Consent Mode
With Google Consent Mode, the behavior changes.
When a visitor lands on your website for the first time, Google Tag Manager and GA4 load immediately, even before analytics cookies have been accepted. Instead of storing cookies, GA4 sends anonymous cookieless pings.
These cookieless pings are primarily used by Google for behavioral and conversion modeling. They are not available as regular events in standard GA4 reports. If you need to analyze this raw event data directly, you can export your GA4 data to BigQuery and query it using SQL.
When the visitor later accepts analytics cookies, GA4 switches from cookieless measurement to cookie-based measurement. Depending on your implementation, this can result in a new session being created.
The problem is that by this point, the original acquisition information may no longer be available. When the visitor clicks another page on your website, the first tracked page in the new cookie-based session has neither UTM parameters nor an external referrer, so GA4 attributes the session as Direct.
The process typically looks like this:
- Visitor lands on the website.
- Google Tag Manager and GA4 load in cookieless mode.
- Anonymous cookieless pings are sent.
- The visitor accepts analytics cookies.
- A cookie-based GA4 session is initialized.
- No new page_view is sent with the original acquisition information.
- The visitor navigates to another page.
- A new page_view starts the tracked session.
- The original source/medium is no longer available.
- GA4 attributes the session as Direct.
How to Preserve the Original Source/Medium
There are multiple ways to solve this problem.
Option 1: Reload the Page After Consent
The simplest solution is to reload the page immediately after the visitor grants analytics consent.
If the landing page contains UTM parameters, the refreshed page starts a new session with those parameters still present in the URL, allowing GA4 to attribute the session correctly.
Although this solution is easy to implement, it has two important drawbacks:
- It only works for traffic that contains UTM parameters.
- It doesn’t preserve attribution for visitors arriving from organic search, because refreshing the same page doesn’t recreate the original referrer.
In addition, automatically refreshing a page immediately after a user action generally creates a poor user experience.
Option 2: Store the Original Source in First-Party Cookies
A much more reliable solution is to capture the original acquisition information before consent is granted and store it in first-party cookies.
Whenever UTM parameters are present in the URL, simply save them into cookies such as:
utm_source
utm_medium
You can easily extend this approach with additional parameters, including:
utm_campaign
utm_term
utm_content
gclid
fbclid
msclkid
The remaining challenge is organic traffic.
Organic visitors don’t arrive with UTM parameters, so your script should also inspect the document referrer. If the visitor came from a known search engine, automatically store values such as:
utm_source = google
utm_medium = organic
The following example demonstrates the basic implementation.
<script>
(function () {
"use strict";
var COOKIE_DAYS = 30;
var SEARCH_ENGINES = [
"google",
"bing",
"yahoo",
"duckduckgo",
"seznam",
"yandex",
"baidu"
];
function getUrlParam(name) {
return new URLSearchParams(window.location.search).get(name);
}
function getCookie(name) {
var match = document.cookie.match(
new RegExp("(?:^|; )" + name + "=([^;]*)")
);
return match ? decodeURIComponent(match[1]) : null;
}
function setCookie(name, value, days) {
var expires = new Date(Date.now() + days * 864e5).toUTCString();
document.cookie =
name +
"=" +
encodeURIComponent(value) +
"; expires=" +
expires +
"; path=/; SameSite=Lax";
}
function getReferrerSearchEngine() {
try {
if (!document.referrer) return null;
var hostname = new URL(document.referrer).hostname.toLowerCase();
for (var i = 0; i < SEARCH_ENGINES.length; i++) {
if (hostname.indexOf(SEARCH_ENGINES[i]) !== -1) {
return SEARCH_ENGINES[i];
}
}
} catch (e) {}
return null;
}
["utm_source", "utm_medium"].forEach(function (key) {
var value = getUrlParam(key);
if (value) {
setCookie(key, value, COOKIE_DAYS);
}
});
if (!getCookie("utm_source")) {
var engine = getReferrerSearchEngine();
if (engine) {
setCookie("utm_source", engine, COOKIE_DAYS);
setCookie("utm_medium", "organic", COOKIE_DAYS);
}
}
})();
</script>
Feel free to extend the script with any additional parameters required by your attribution model and adjust the cookie lifetime to suit your reporting needs.
To verify that everything works correctly, search for your website in Google (or another supported search engine), click the result, and then open Developer Tools → Application → Cookies. You should see the stored attribution values.
Pass First-Party Cookies to GA4
The next step is to make these values available inside Google Analytics 4.
First, create Google Tag Manager variables for every cookie you’ve created.
Navigate to:
Workspace → Variables → User-Defined Variables → New
Configure the variable as follows:
Variable Type: First-Party Cookie
Cookie Name: utm_source
Repeat the same process for every cookie you want to send to GA4.
Next, open your GA4 Configuration tag and add new Configuration Parameters (or Configuration Settings, depending on your GTM version).
For example:
original_source: {{utm_source}}
original_medium: {{utm_medium}}
Repeat this pattern for all additional attribution parameters.
The parameter names are entirely up to you. Using names such as original_source and original_medium is recommended because they clearly distinguish your custom attribution fields from GA4’s built-in traffic source dimensions.
After publishing your container, you should be able to confirm that these parameters are being sent in the Realtime report or DebugView.
Create Custom Dimensions in GA4
Sending custom event parameters to GA4 doesn’t automatically make them available in reports.
To use them throughout GA4, you’ll need to create Custom Dimensions.
Navigate to:
Admin → Data Display → Custom Definitions → Create Custom Dimension
Configure each dimension like this:
Dimension name: Original Source
Scope: Event
Description: Optional
Event parameter: original_source
Repeat this process for every custom parameter you’ve created.
Keep in mind that newly created event parameters may not appear immediately in the Custom Definition interface. Standard GA4 reports usually take several hours before new parameters become available.
Once the custom dimensions are active, you’ll be able to use them in:
- Standard reports
- Explorations
- Comparisons
- Filters
- Audiences
just like any other event-scoped dimension.
Limitations
This solution preserves the visitor’s original acquisition data for your own reporting, but it does not overwrite GA4’s built-in traffic acquisition dimensions.
Instead, it creates your own attribution fields (such as original_source and original_medium) that can be analyzed through Custom Dimensions, Explorations, etc.
This gives you full control over attribution while keeping your implementation compatible with Google Consent Mode.