Every analytics tag is a request, some bytes and some work on the main thread. Here are the real numbers for one cookieless tracker, the honest limits of the comparison, and a method to price any script on your own pages.
We measured the GhostMetrics tracker on November 4, 2026: gm.js is 8,420 bytes uncompressed, 2,459 bytes gzip and 2,485 bytes brotli as served from the edge, so about 2.5 KB on the wire. In our own earlier measurement, Google's gtag.js came to roughly 150 KB compressed. That is about sixty times the bytes.
defer keeps the tag off the render path. A deferred script downloads in parallel with parsing and runs after the document is parsed.Jump to the measurement how-to, or open the live demo and inspect the tracker yourself.
An analytics tag is third-party-style code that runs on every page of your site. Whatever it costs, it costs on every visit. That is why script weight shows up in conversations about Core Web Vitals, the set of field metrics Google uses to describe loading speed, visual stability and responsiveness. Three of them are relevant here.
Largest Contentful Paint (LCP) measures when the main content of the page appears. A tag can delay it by competing for bandwidth early in the load, by blocking parsing if it is loaded synchronously, or by occupying the main thread when the browser wants to paint. Cumulative Layout Shift (CLS) measures unexpected movement. Analytics tags themselves rarely shift layout, but consent banners and widgets that load alongside them often do. Interaction to Next Paint (INP) measures how quickly the page responds to input, and long-running script work on the main thread is a classic cause of poor INP.
The effect of one small tag on any of these is usually modest. The effect of ten tags is not. Analytics, a tag manager, ad pixels, a chat widget, a heatmap tool and an A/B test snippet each look reasonable alone and add up together. Treating analytics as the cheapest tag to remove is wrong in the other direction too: a tracker that weighs a few kilobytes is not where a slow page comes from. The useful question is not "is analytics slow?" but "what does this tag cost, measured on my page?" For the field-measurement side, see Core Web Vitals and real-user monitoring.
These figures come from requesting the live tracker file on November 4, 2026 with curl, once per encoding. The first request asked for a gzip response, the second for brotli, and the third decoded the response to count the raw bytes.
| Measure | gm.js size | How we got it |
|---|---|---|
| Uncompressed | 8,420 bytes | Decoded response body length |
| Gzip | 2,459 bytes | Request with Accept-Encoding: gzip |
| Brotli | 2,485 bytes | Request with Accept-Encoding: br |
Two things in that table are worth a note. Brotli is usually smaller than gzip, but for a file this small the difference is within a few dozen bytes and can go either way, so we report both rather than pick the flattering one. And the number that reaches the browser depends on what the visitor's browser asks for, so "about 2.5 KB on the wire" is the honest summary. The script is served from Cloudflare's edge with a short browser cache, so repeat visits within the cache window do not download it again.
What does the tracker do with those bytes? It reads the privacy flags, builds a pageview payload, attaches a few listeners for scroll, visibility changes and clicks on elements with a data-gm-event attribute, observes paint, layout-shift and navigation timing entries, and sends small JSON payloads with navigator.sendBeacon, falling back to fetch with keepalive. Sending with a beacon matters for speed: the browser queues it without holding up navigation or the main thread waiting for a response.
It is fair to ask whether size is the right proxy. A small file can still do expensive work, and a large file that mostly sits idle can be cheap. We chose to report bytes because they are exact and reproducible; for execution cost, use the profiling method below on your own pages rather than relying on a number from us.
Gtag, the tag behind Google Analytics 4, is a general-purpose loader that also serves ads, conversion tracking and other Google products, and it can pull additional resources depending on what you configure. In our own measurement it came to roughly 150 KB compressed, which is why GA4 shows up so often in page-speed audits. That figure is a point-in-time measurement of a file Google updates, so re-measure it on your site with the method below rather than quoting ours.
For the other tools, we do not publish byte counts we have not measured. The table compares the dimensions that are well established and leaves the rest to your own test.
| Tool | Script size | Loads from | What to confirm yourself |
|---|---|---|---|
| GhostMetrics | 8,420 bytes raw, about 2.5 KB compressed (measured) | Cloudflare edge, one file | Requests after load (pageview, leave, vitals) |
| GA4 (gtag.js) | Roughly 150 KB compressed in our measurement | Google's tag domain | Extra requests and cookies set after load |
| Plausible | Check the current script size | Plausible or your own proxy | Size of the variant you enable |
| Fathom | Check the current script size | Fathom or a custom domain | Size on your plan's script |
| Umami | Check the current script size | Your server or Umami Cloud | Size of the tracker you configure |
| Cloudflare Web Analytics | Check the current beacon size | Cloudflare | Whether it is injected or added by hand |
Notice what the table does not claim: it does not say the smallest script wins. A larger tag can buy you things a small one cannot, such as ad attribution, audiences and ecommerce funnels, which are real strengths of GA4 that a cookieless tool does not try to match. If you need those, the weight is the price. If you mostly need traffic sources, top pages, events and vitals, you are paying for features you do not use. Our GA4 alternative guide walks through that decision, and cookieless analytics compared covers the field.
Four other costs hide behind a file size, and a fair comparison has to consider them.
Connections. A tag served from a domain the page has not already contacted forces the browser to do DNS, TCP and TLS work first. Hosting the script on the same origin as the page, or on a host you already use, can remove that cost. Most hosted analytics offer a custom-domain or proxy option; check each vendor's docs for the setup.
Execution. JavaScript costs parse and compile time as well as download time, and phones pay more than laptops. A modest script that runs for a few milliseconds is fine; a tag that runs long tasks during load competes with your own code. This is why the profiling step below matters more than the byte count.
Follow-up requests. The first request is not the last. A tag that fires a pageview on load, another on scroll and another on exit makes several requests per visit, each cheap but not free. The GhostMetrics tracker sends a pageview when the page loads, a leave request when the tab is hidden or the page unloads, a vitals report on the same trigger, and an event request for each click on a tagged element. Those go out as beacons and are not on the critical path.
Side effects. Cookies, local storage writes and consent banners all add work and sometimes layout shift. A cookieless tracker that adds no banner removes a source of CLS that has nothing to do with its own file size. See analytics without a consent banner for that angle.
This works for any tag, ours included. Use a desktop Chrome or Edge profile with extensions disabled, since extensions add their own requests and skew the result.
A quick command-line check also works for the file itself: request the script with curl, ask for gzip with Accept-Encoding: gzip, and count bytes with wc -c. That is how the figures above were produced.
curl -s -H 'Accept-Encoding: gzip' https://ghostmetrics.nullagency.io/gm.js | wc -c
curl -s --compressed https://ghostmetrics.nullagency.io/gm.js | wc -c
The first command prints the compressed length and the second prints the uncompressed length. Swap in any script URL to price another tag the same way.
A lightweight tag is only half of a speed story; the other half is seeing how real visitors experience your pages. GhostMetrics captures four field metrics from visitors' browsers using the standard performance APIs: First Contentful Paint, Largest Contentful Paint, Cumulative Layout Shift and Time to First Byte, shown per page in the dashboard's vitals table and per device in a Performance by Device view. TTFB follows Google's definition, measured from the start of navigation to the first byte of the response, so redirects, DNS, connection setup and server time are all inside it.
It does not capture Interaction to Next Paint. That is a real gap for this topic, because INP is the metric most likely to expose a heavy script. If you want INP, use a browser tool like Lighthouse in timespan mode or another field tool alongside it. We would rather say that here than let you infer coverage we do not have.
Another honest limit: visitors who send Global Privacy Control or Do Not Track are not measured at all, so vitals reflect the visitors who did not opt out. And because the tracker runs in the same page it measures, it observes entries like any other script; it cannot give you a "without analytics" baseline. For that you need the with-and-without comparison in the how-to above. Read the static site install guide if you want to see the one-tag setup with defer that this page assumes.
When should script size drive your decision? If you serve users on slow mobile networks, if your pages are mostly static and fast so a tag is a large share of the total, or if you are fighting for a Core Web Vitals pass on the margin, bytes matter. If your pages already ship megabytes of images and framework code, switching analytics tags will not be the lever that moves your scores. Measure first, then decide. Our guide to measuring website traffic covers what to look at once the tag is in place.
GhostMetrics is cookieless web analytics installed with one script tag, with FCP, LCP, CLS and TTFB captured from real visitors in the dashboard (not INP). The free plan is free forever: one website, unlimited pageviews, real-time dashboard, every dashboard view, full history. You can price the tag on your own pages with the method above.
Pro is $9.99/mo flat with unlimited pageviews, unlimited websites (fair-use ceiling of 250), a public shareable dashboard and a 30-day free trial (card required to start, nothing charged for 30 days, cancel anytime). Raw CSV or JSON export is on every plan, including free. Current details are on the pricing page.
Start free — no card See the live demoDisclosure: GhostMetrics is built by Null Agency and we use it on this site, so we are not a neutral party about our own product. The byte counts for gm.js were measured by us on the date given; the gtag figure is our own earlier measurement and other vendors' sizes are left for you to check. Script sizes change with releases, so measure before you decide.