What gtag.js on Your Page and in Your GTM Container Does to Your Data
A site loads Google's gtag.js tracking library from a hardcoded script tag in the page template. The same site also has a GTM container with GA4 tags that load gtag.js independently through the Google Tag. Both instances fire page_view events. Both send data to the same GA4 measurement ID. GA4 counts both.
This is double-tagging. It's the most common source of inflated analytics data, and it typically happens when different people manage different parts of the tracking infrastructure without coordinating.
How double-tagging happens
The most common path is migration. A site starts with gtag.js installed directly in the page template, the way Google's GA4 setup wizard recommends for sites without a tag manager. Later, someone adds GTM to centralize tag management. They create GA4 tags inside GTM. Nobody removes the original gtag.js snippet from the page template because nobody remembers it's there, or because a different team manages the site template.
CMS plugins create the same problem. WordPress plugins like Google Site Kit or MonsterInsights inject gtag.js automatically. If the marketing team also deploys GA4 through GTM, two independent tracking instances exist. The plugin doesn't know about GTM. GTM doesn't know about the plugin. Both fire.
Website redesigns are another source. A developer rebuilds the site, copies the existing <head> content (including the gtag.js snippet) into the new template, and the marketing team re-installs GTM with fresh GA4 tags. The old snippet survives in the new template alongside the new GTM implementation.
In each case, the page loads two independent GA4 tracking instances pointing at the same measurement ID. Each instance initializes separately, creates its own session context, and sends its own events. GA4 receives both streams and processes them independently.
What doubled data looks like
The symptoms are specific and recognizable.
Page view counts approximately double. If the site has 50,000 real page views per month, GA4 reports 95,000-100,000. The doubling isn't always exact because the two tracking instances may fire at slightly different times, and one may fail occasionally due to ad blockers, network issues, or script loading order.
Session counts inflate. Each tracking instance may create its own session context, depending on how the client IDs are managed. In the worst case, a single user visit generates two sessions. In less severe cases, the sessions merge (if both instances use the same client ID cookie) but the doubled events within the session still inflate event counts and engagement metrics.
Bounce rate drops below realistic levels. GA4 considers a session "engaged" if it lasts more than 10 seconds, has more than one page view, or triggers a conversion event. With doubled page views, every session has at least two page_view events, which means every session qualifies as engaged. Bounce rates below 10% on a site that should be seeing 40-60% are a strong indicator of duplicate tracking.
Revenue appears inflated in ecommerce. If both tracking instances fire purchase events, GA4 counts the transaction twice. Unlike most events, GA4 can deduplicate purchase events if both send the same transaction_id parameter. But this only works when both instances include the transaction ID, and when the ID is identical. Hardcoded gtag.js implementations often don't include transaction IDs, which means the deduplication safety net doesn't apply.
"Direct" traffic is over-reported. When two tracking instances create separate sessions for the same visit, one may correctly attribute the traffic source while the other defaults to "Direct" because it initialized after the referrer information was consumed by the first instance. This inflates the Direct channel in traffic reports.
How to find it
Check the page source. View the source of any page on the site and search for gtag. If you find a script tag loading https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXX AND a GTM container snippet (https://www.googletagmanager.com/gtm.js?id=GTM-XXXXXX), both are present. The gtag.js script is the hardcoded instance. The GTM container may also load gtag.js internally through Google Tag tags.
Check the Network tab. Open browser developer tools, go to the Network tab, reload the page, and filter for requests to google-analytics.com/g/collect. If you see two page_view requests with the same measurement ID firing within milliseconds of each other, both instances are active.
Check GA4 DebugView. Enable Debug mode (via the GA Debugger extension or by adding debug_mode: true to your GA4 configuration) and navigate to Admin > DebugView. If page_view events appear twice in rapid succession on each page navigation, two instances are firing.
Compare GA4 data against a known source of truth. For ecommerce, compare GA4 purchase counts against the payment processor's transaction count. If GA4 shows roughly twice the payment processor's count, double-tagging is likely. For lead generation, compare GA4 form submission events against the CRM's new lead count. A 2:1 ratio is the signature of duplicate tracking.
How to fix it
Remove the hardcoded gtag.js snippet. GTM should be the single source of truth for all tracking. If GA4 is configured inside GTM with proper Google Tag and GA4 Event tags, the hardcoded gtag.js script in the page template is redundant. Remove the <script> tag that loads gtag.js and the gtag('config', ...) call.
If the hardcoded snippet was added by a CMS plugin, disable the plugin's tracking feature or uninstall the plugin. Google Site Kit, MonsterInsights, and similar plugins typically have a setting to disable their built-in tracking if GTM is managing GA4.
If removing the hardcoded snippet isn't immediately possible (the template is managed by a different team, the CMS plugin is also used for other purposes), there's a GA4 data stream setting that helps with one specific case: "More tagging settings > Ignore duplicate instances of on-page configuration." This prevents duplicate page_view events specifically when two gtag('config', ...) calls target the same measurement ID. It does NOT prevent duplication of other events (scroll, click, form submissions), so it's a partial fix, not a solution.
Validate after removal. After removing the hardcoded snippet, check that GA4 still receives data (the GTM implementation should handle everything), that event counts drop to expected levels (approximately half the previous inflated counts), and that bounce rate returns to a realistic range (typically 30-60% for B2B SaaS marketing sites).
The transition period will show a visible drop in GA4 reports. Page views cut in half, sessions drop, engagement metrics normalize. This looks alarming in reports but represents the data becoming accurate rather than the site losing traffic. Document the date of the change so future analysts understand the discontinuity.
Preventing double-tagging
The root cause is lack of coordination between the people who manage the site template and the people who manage GTM. The fix is organizational.
Single ownership of tracking. One team or one person is responsible for all tracking implementation, whether it lives in the page source, in GTM, or in a CMS plugin. If the development team adds gtag.js and the marketing team adds GTM, nobody is checking for overlap.
Tracking inventory. Know what's on the page. A quarterly check of the page source for tracking scripts (gtag.js, fbq(), lintrk(), and similar) alongside a review of the GTM container catches overlaps before they accumulate months of duplicate data.
GTM as the single source. If GTM is installed, all tracking should flow through GTM. No hardcoded tracking scripts in the page template except the GTM container snippet itself (and optionally, consent defaults that need to load before GTM). This is a policy decision that prevents double-tagging by eliminating the conditions that cause it.