{"id":1119,"date":"2026-05-20T15:24:33","date_gmt":"2026-05-20T13:24:33","guid":{"rendered":"https:\/\/www.sabatka.net\/cs\/?p=1119"},"modified":"2026-05-20T15:24:35","modified_gmt":"2026-05-20T13:24:35","slug":"gtm-page-speed-impact","status":"publish","type":"post","link":"https:\/\/www.sabatka.net\/en\/gtm-page-speed-impact\/","title":{"rendered":"GTM Performance Audit: 33 KB Container vs. 95 KB Facebook Pixel"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><em>GTM gets blamed for slow sites. But GTM is just the messenger \u2014 the real culprits are the scripts you load through it. Here&#8217;s which ones cause the damage, and how to measure it with Chrome DevTools.<\/em><\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Website speed = business<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Site speed stopped being a technical concern the moment Google built it into ranking. Core Web Vitals are now a direct signal \u2014 and e-commerce data consistently shows roughly 7 % fewer conversions for every extra second of load time. Those seconds tend to show up as TBT (Total Blocking Time) in your PageSpeed report.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">GTM is the usual suspect. You add it, the site slows down, the conclusion seems obvious. The conclusion is wrong.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Google Tag Manager itself adds approximately 33 KB to your page. That&#8217;s less than a typical SVG icon. The actual slowdown comes from the scripts you fire through GTM \u2014 and it depends less on how many you have and more on how you&#8217;ve configured them.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button scroll_to_subscribe\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/www.sabatka.net\/en\/kontakt\/\">Contact me<\/a><\/div>\n\n\n\n<div class=\"wp-block-button linkedinShare\"><a class=\"wp-block-button__link wp-element-button\">Share on LinkedIN<\/a><\/div>\n<\/div>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">GTM container vs. the scripts inside<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">An empty GTM container loads, parses, and executes. That&#8217;s it \u2014 its influence ends there. What happens next depends entirely on the tags inside.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th><strong>Script<\/strong><\/th><th><strong>Size (gzip)<\/strong><\/th><th><strong>Note<\/strong><\/th><\/tr><\/thead><tbody><tr><td>GTM container (empty)<\/td><td>~33 KB<\/td><td>Minimal impact<\/td><\/tr><tr><td>GA4 (gtag.js)<\/td><td>~96 KB<\/td><td>One of the heaviest in a typical stack<\/td><\/tr><tr><td>Facebook\/Meta Pixel<\/td><td>~95 KB<\/td><td>Plus 4 HTTP requests, +300 ms TBT<\/td><\/tr><tr><td>Hotjar (heatmaps)<\/td><td>~61 KB<\/td><td>With Survey module: up to 230 KB<\/td><\/tr><tr><td>Microsoft Clarity<\/td><td>~25 KB<\/td><td>Lightest option, asynchronous<\/td><\/tr><tr><td>LinkedIn Insight Tag<\/td><td>~19 KB<\/td><td>Relatively lean<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">A typical mid-size e-commerce stack: GTM + GA4 + Meta Pixel + consent management. That&#8217;s 400\u2013600 KB of JavaScript before the page feels usable.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">What actually slows down GTM \u2014 implementation mistakes<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Google publishes <a href=\"https:\/\/web.dev\/articles\/tag-best-practices\" target=\"_blank\" rel=\"noopener\">tag best practices<\/a> and <a href=\"https:\/\/support.google.com\/tagmanager\/answer\/2772488\" target=\"_blank\" rel=\"noopener\">GTM performance docs<\/a>. Here&#8217;s what they mean in practice.<\/p>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Templates, not Custom HTML<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A Custom HTML tag is a black box \u2014 GTM drops it into the page without visibility into what it does next. It can chain-load scripts, manipulate the DOM, spawn further requests. A GTM template is controlled: GTM knows exactly what API calls it needs and can schedule them efficiently. One predictable package instead of a cascade.<\/p>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Trigger timing<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Most tags fire on Page View because &#8220;that&#8217;s how it was set up.&#8221; But Page View fires before the page renders. For analytics, heatmaps, and remarketing pixels, DOM Ready is a reasonable compromise. Exception: conversion tracking (purchases) should fire as early as possible.<\/p>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Variable dependency chains<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">When variable A depends on B, which depends on C, GTM evaluates the entire chain synchronously. I&#8217;ve seen containers where this added over 200 ms to every page view.<\/p>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Paused tags aren&#8217;t in the container<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A blocking trigger (exception) keeps the code in the container even when the tag doesn&#8217;t fire.<\/p>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Unused variables<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Minor impact, but <strong>unused variables stay in the compiled container <\/strong>and get evaluated at init. Worth cleaning up.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">GTM has other concerns beyond performance \u2014 the <a href=\"https:\/\/www.sabatka.net\/en\/gtm-v-rozporu-s-gdpr\/\">Hannover court ruling on GTM and GDPR<\/a> is worth reading.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Is there a problem? And how big?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">First, find out if there&#8217;s anything to fix. Open <a href=\"https:\/\/pagespeed.web.dev\/\" target=\"_blank\" rel=\"noopener\">PageSpeed Insights<\/a>, enter your URL, and check if it reports third-party script issues. If not \u2014 don&#8217;t bother. Focus on something that actually moves the needle, like content or conversions.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If PageSpeed does flag problems, measure how much your container weighs. I built a playground at <a href=\"http:\/\/sandbox.sabatka.net\/speed\/start\" target=\"_blank\" rel=\"noopener\"><strong>sandbox.sabatka.net\/speed\/start<\/strong><\/a> \u2014 it compares your configuration against an empty container with just a basic GA4 tag. You&#8217;ll see the relative difference. It&#8217;s not a precise benchmark, but you&#8217;ll immediately know whether the problem is negligible or not.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"http:\/\/sandbox.sabatka.net\/speed\/start\" target=\"_blank\" rel=\"noopener\">GTM speed sandbox<\/a><\/div>\n<\/div>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Step 1: Copy your production container.<\/strong> Never test in production. Remove or deactivate production IDs (GA4 Measurement ID, Meta Pixel ID, etc.) \u2014 otherwise you&#8217;ll send test data to live accounts, and GA4 doesn&#8217;t offer a clean way to filter that out retroactively.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Step 2: Set up test conditions.<\/strong> Open DevTools (F12) and configure realistic conditions:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Network<\/strong> tab \u2192 check <strong>Disable cache<\/strong> (simulates a first visit)<\/li>\n\n\n\n<li><strong>Performance<\/strong> tab \u2192 <strong>CPU throttling<\/strong> to 4x slowdown (simulates an average Android phone, not your M3 MacBook)<\/li>\n\n\n\n<li><strong>Network<\/strong> tab \u2192 <strong>Network throttling<\/strong> to Fast 4G<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Your users on mobile are in exactly these conditions \u2014 or worse.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Warning:<\/strong> Never use production IDs in the playground. It&#8217;s for orientation, not live data.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">If the difference is small, you probably don&#8217;t need to go further. Clean up the container once in a while \u2014 remove unused tags, clean out orphaned variables, move analytics and remarketing tags from Page View to DOM Ready. That alone often does the job.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-794e3cfa wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:33.33%\">\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"1024\" height=\"1024\" data-src=\"https:\/\/www.sabatka.net\/wp-content\/uploads\/2026\/04\/null-did-you-know.png\" alt=\"\" class=\"wp-image-936 lazyload\" data-srcset=\"https:\/\/www.sabatka.net\/wp-content\/uploads\/2026\/04\/null-did-you-know.png 1024w, https:\/\/www.sabatka.net\/wp-content\/uploads\/2026\/04\/null-did-you-know-300x300.png 300w, https:\/\/www.sabatka.net\/wp-content\/uploads\/2026\/04\/null-did-you-know-150x150.png 150w, https:\/\/www.sabatka.net\/wp-content\/uploads\/2026\/04\/null-did-you-know-768x768.png 768w\" data-sizes=\"(max-width: 1024px) 100vw, 1024px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1024px; --smush-placeholder-aspect-ratio: 1024\/1024;\" \/><\/figure>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:66.66%\">\n<h3 class=\"wp-block-heading\"><strong>Lab note<\/strong><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">This is the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Observer_effect_(physics)\" target=\"_blank\" rel=\"noopener\">Observer Effect<\/a> \u2014 a principle from quantum physics where the act of measuring influences what&#8217;s being measured. Every tag you add to measure your site slows down the very site you&#8217;re trying to measure. The more you measure, the slower your site gets.<\/p>\n<\/div>\n<\/div>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Going deeper \u2014 Chrome DevTools<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If every millisecond matters, you&#8217;re building a measurement framework, or you want your container tuned to the max, open Chrome DevTools.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Step 1:<\/strong> Set up Chrome DevTools (F12) with slower connection \u2014 in the <strong>Network<\/strong> tab enable <strong>Disable cache<\/strong> and <strong>Network throttling<\/strong> to Fast 4G, in the <strong>Performance<\/strong> tab set <strong>CPU throttling<\/strong> to 4x slowdown. Same setup as the previous test.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Step 2: Measure baseline.<\/strong> Test the page with GTM loaded but empty (all tags paused). Record:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/web.dev\/articles\/tbt\" target=\"_blank\" rel=\"noopener\"><strong>TBT<\/strong><\/a> <strong>(Total Blocking Time)<\/strong> \u2014 time the main thread blocks interaction<\/li>\n\n\n\n<li><a href=\"https:\/\/web.dev\/articles\/lcp\" target=\"_blank\" rel=\"noopener\"><strong>LCP<\/strong><\/a> <strong>(Largest Contentful Paint)<\/strong> \u2014 when the largest visible element renders<\/li>\n\n\n\n<li><strong>Waterfall in the Network tab<\/strong> \u2014 load order and duration of scripts<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Step 3: Enable tags one by one.<\/strong> Activate tags individually, measure after each, log the delta. It&#8217;s the only way to know which specific script is responsible for what.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Step 4: Compare &#8220;with tags&#8221; vs. &#8220;without tags.&#8221;<\/strong> The difference in TBT and LCP is your actual &#8220;measurement tax&#8221; \u2014 the price you pay for tracking.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">At this level, structural changes start to make sense: rewrite Custom HTML tags as templates, eliminate variable dependency chains, or move to <a href=\"https:\/\/www.sabatka.net\/en\/co-to-je-server-side-mereni-sgtm\/\">server-side tracking<\/a> which shifts processing from the browser to a server.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For common tracking mistakes found in these audits, see my <a href=\"https:\/\/www.sabatka.net\/en\/bugs-in-tracking-measurecamp-czechia-2025\/\">MeasureCamp Czechia presentation<\/a>.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Bottom line<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GTM itself doesn&#8217;t slow your site in any meaningful way. The scripts inside it do \u2014 and you&#8217;d be loading those scripts regardless, whether through GTM or hardcoded in HTML. Or you could stop doing marketing altogether \u2014 then your site will be blazing fast \ud83d\ude09<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Configuration matters too \u2014 wrong trigger timing, unmaintained variables, and dependency chains can add seconds in extreme cases.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Run the test on your site. If you need help with the results or want a GTM container audit, <a href=\"https:\/\/www.sabatka.net\/en\/kontakt\/\">get in touch<\/a>.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button scroll_to_subscribe\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/www.sabatka.net\/en\/kontakt\/\">Contact me<\/a><\/div>\n\n\n\n<div class=\"wp-block-button linkedinShare\"><a class=\"wp-block-button__link wp-element-button\">Share on LinkedIN<\/a><\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>GTM gets blamed for slow sites. But GTM is just the messenger \u2014 the real culprits are the scripts you load through it. Here&#8217;s which ones cause the damage, and how to measure it with Chrome DevTools. Website speed = business Site speed stopped being a technical concern the moment Google built it into ranking. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1129,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9,10],"tags":[],"class_list":["post-1119","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-digital-analytics","category-google-tag-manager-gtm"],"_links":{"self":[{"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/posts\/1119","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/comments?post=1119"}],"version-history":[{"count":11,"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/posts\/1119\/revisions"}],"predecessor-version":[{"id":1132,"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/posts\/1119\/revisions\/1132"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/media\/1129"}],"wp:attachment":[{"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/media?parent=1119"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/categories?post=1119"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/tags?post=1119"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}