Skip to main content
ASoc
Comparison

Fathom vs Matomo: Who Runs the Server, and What It Costs Your CSP

Hosted or self-hosted is the real axis. The CSP cost of each, measured against a live policy, plus why script weight is the wrong thing to compare.

The ASoc Team10 min read

Both Fathom and Matomo exist because Google Analytics asks you to put a consent banner on your own site. Both are cookieless-capable and GDPR-friendly. The difference that decides it is not the dashboard: Fathom is a hosted product you buy, and Matomo is a platform you can run yourself — which means one of them adds a subscription and the other adds a server.

The short answer

Choose Fathom if you want privacy-friendly pageviews with no infrastructure to operate and a dashboard nobody needs training for. Choose Matomo if you need session-level depth (funnels, heatmaps, visitor profiles, goals), or if "the data must live on hardware we control" is a requirement rather than a preference — Matomo self-hosts, Fathom does not.

Everything below that is a consequence of those two sentences.

The axes that actually diverge

Fathom AnalyticsMatomo
Who runs itFathom — hosted onlyYou (self-hosted) or Matomo Cloud
Self-host the full productNoYes
Source modelProprietary (a cut-down Lite edition is open source)Open source
InstallA script tag carrying a site IDA script pointed at your Matomo instance
Data depthPageviews, referrers, goalsFunnels, heatmaps, session recording, goals, A/B testing, an API
Data locationFathom's infrastructureYour server, or Matomo's cloud
CookiesCookieless by defaultConfigurable; cookieless mode available
Consent bannerNot required for the default setupDepends which features you enable
CSP costOne script host + one beacon host, or zero cross-origin via custom-domain routingYour own origin (self-hosted) or Matomo's (cloud), on script-src and connect-src
Ongoing workNoneUpdates, backups and uptime for an analytics server, if self-hosted
Portable off your hostYesYes

Pricing on both moves often enough that quoting today's numbers would mislead you by the time you read this. Check their own pricing pages, and compare on pageviews or events per month rather than on seat count — that is the axis both bill on.

Matomo Cloud narrows the infrastructure gap to nothing, for a subscription. What it does not narrow is the depth gap or the CSP cost: it is the same tracking script and the same dashboard, just not one you patch.

Disclosure, because it changes how to read this

This storefront runs neither. It runs Vercel Web Analytics, because it deploys on Vercel and its entire reporting need is five named conversion events. We evaluated both of the tools above during that decision, which is where the measurements below come from — but nobody should take "we picked a third thing" as a verdict on either.

If your situation is close to ours, the two posts that cover those matchups directly are Fathom vs Vercel Analytics and Vercel Analytics vs Matomo. This post is for the case where Vercel is not in the picture and the real question is hosted-versus-self-hosted.

The cost neither vendor's comparison page mentions: your CSP

If you ship a Content-Security-Policy, adding analytics is a policy edit, and the size of that edit differs between these two. Here is the live policy from this project's next.config.ts, unabridged:

const contentSecurityPolicy = [
  "default-src 'self'",
  `script-src 'self' 'unsafe-inline'${scriptSrcEval} https://app.lemonsqueezy.com https://assets.lemonsqueezy.com https://va.vercel-scripts.com`,
  "style-src 'self' 'unsafe-inline'",
  "img-src 'self' data: blob: https:",
  "font-src 'self' data:",
  `connect-src 'self' ${supabaseOrigin}`,
  "frame-src 'self' https://*.lemonsqueezy.com https://*.asoctemplates.com https://*.vercel.app",
  "frame-ancestors 'none'",
  "base-uri 'self'",
  "form-action 'self' https://*.lemonsqueezy.com",
  "object-src 'none'",
].join("; ");

Two directives matter for analytics, and a tracker almost always wants both: script-src to load its JavaScript, and connect-src for the beacon that reports events back. The file's own rule is "each verified against the code that loads it — do not add speculative hosts," which is the discipline that makes this comparison concrete:

  • Fathom, default install: one script-src host for the script, and a connect-src entry for the collector. Two allowances.
  • Fathom with custom-domain routing: you point a subdomain of your own at their collector. Both the script and the beacon become first-party, so 'self' already covers them — zero new hosts, and as a side effect the requests are much harder for a blocker to drop.
  • Matomo, self-hosted: your Matomo instance is your own origin, so the same zero-new-host logic applies if it lives on a subdomain of the site. That is self-hosting's quiet CSP win.
  • Matomo Cloud: your instance's cloud hostname, on both directives.

The pattern: whichever tool you can serve from your own domain is the one your policy likes, and both of these offer a route to that. The tool that cannot be made first-party is the one you end up writing exceptions for.

One more directive is worth checking before you commit: connect-src also governs WebSockets. This policy deliberately has no wss: entry, documented in the file as "No realtime is used (no .channel( anywhere), so no wss: is needed." If a tool you are evaluating opens a socket for live-visitor views, that is another allowance, and it is the kind of thing you discover from a console error on a Friday rather than from a feature list.

Script weight is the wrong axis, and we can show why

Comparisons of privacy analytics love the script-size table — one tool's tracker is a few kilobytes, another's is tens. It is a real difference and, on a React site, it is usually irrelevant.

The measured reason, from this project's Lighthouse pass: mobile performance sits at 89–99 across the eight main pages while desktop is 100 everywhere, and the remaining mobile gap is not bytes on the wire. With every image and every prefetch blocked, the home page's LCP moved from 3786 ms to 3740 ms — 46 ms. The cost is JavaScript execution, and the single largest chunk is react-dom at 221 KiB raw / 70 KiB gzipped. That is the framework floor for an interactive storefront.

Against that floor, the delta between two analytics scripts is noise. Weigh instead:

  • Whether it is render-blocking. Any tracker loaded async/defer from a fast host costs essentially nothing; one that is not will cost more than its file size suggests.
  • Whether it is same-origin. A third-party DNS lookup and TLS handshake on a cold connection is usually worth more milliseconds than the script.
  • Whether its failure is visible. Which brings us to the one audit this site does not score 100 on.

The 96 that is really an analytics finding

Accessibility and SEO are 100 on every page, both form factors. Desktop performance is 100 across the board. Best Practices is 96, and the single failing audit is errors-in-console — entirely the Vercel Web Analytics beacon 404-ing when the production build runs on localhost, because the script is injected at the edge only on a real deployment. On the deployed site it returns to 100.

It is a local-measurement artifact, and it is also the general lesson about third-party analytics: when the collector is unreachable, the failure surfaces in your console and in your audits, not in the vendor's dashboard. Every environment where the tracker cannot phone home — local builds, CI, a preview behind auth, a visitor with a blocker — produces that noise. Both tools here have a first-party answer (custom-domain routing; self-hosting on a subdomain), and that is a better reason to use those features than blocker evasion.

What you are actually buying with Matomo's depth

Matomo's feature list is longer in a way that is easy to over-value. The honest test is whether your event vocabulary needs session-level analysis at all.

Ours does not. The whole analytics surface of this site is one typed union in src/lib/analytics.ts — five conversion events (preview_opened, buy_clicked, waitlist_joined, blog_cta_clicked, wishlist_added), each carrying a couple of string properties, wrapped so a typo cannot invent a sixth and so a throw inside a click handler can never cost the sale it was measuring. For a catalogue of 111 products and a few hundred articles, the question we need answered is "which article sent a buyer to which product page", and that is a group-by on two properties, not a funnel.

If your questions sound like where in the five-step signup do people drop out, what did the user click before rage-quitting, or which variant won — Matomo answers those and Fathom does not pretend to. If they sound like how many people read this and clicked through, Matomo's depth is a server you maintain for reports you will not open.

A useful exercise before choosing either: write your event list down. If it fits on one screen, buy the simple tool.

Troubleshooting

SymptomCauseFix
Script blocked, Refused to load in consoleThe tracker's host is not in script-srcAdd the host, or serve it first-party via custom-domain routing / self-hosting
Script loads, no data arrivesThe beacon destination is missing from connect-srcAdd the collector host; it is a separate directive from the script
Console 404s for the tracker in local buildsThe collector only exists in the deployed environmentExpected; it is also why a local Best Practices score can read low
Numbers much lower than server logsBlockers dropping third-party requestsServe the tracker from your own domain — both tools support this
Self-hosted Matomo slows down over timeRaw data growth plus archiving on requestRun the archive cron job; it is not optional at traffic
A "cookieless" setup still triggers a consent reviewAn enabled feature reintroduced identifiersAudit which features you turned on; cookieless is a configuration, not a brand
Live-visitor view silently empty under CSPA WebSocket blocked by connect-srcAllow the wss: origin explicitly, or drop the feature
Analytics works locally, missing in productionThe site ID lives in a build-time env var that was never set on the hostSet it in the hosting project and rebuild — see below

Frequently asked questions

Is Matomo free? Self-hosted Matomo is free to download and open source; it is not free to run. The bill moves from a subscription line to a server, a database, updates, backups and the archiving job. Matomo Cloud exists precisely for teams that want the features without that. Fathom has no free self-hosted path to the full product.

Which one is better for GDPR? Both are built for it, and neither makes you compliant by itself. Self-hosted Matomo gives you the strongest story available here — the data never leaves infrastructure you control — but you inherit responsibility for securing it. Fathom's default is cookieless and anonymous, which for most sites removes the banner. Read each vendor's current data-processing terms; this is the one area where a blog post is the wrong source.

Will my site ID end up in the browser? Yes, and that is fine — it is in the script tag either way. What matters in a framework app is when it gets there. In Next.js a NEXT_PUBLIC_* value is inlined into the bundle when the build runs, so changing it on your host does nothing until you rebuild. That trips people up on exactly this kind of integration; the Next.js env rules walk through the three moments a value is read.

Can I run both while deciding? For a few weeks, yes, and it is the only way to compare numbers you will actually trust. Expect the totals to disagree — different bot filtering, different session rules, different blocker exposure. Compare trends and relative page rankings, not absolute visitor counts.

Templates in this post

ASoc Lens is a product-analytics SaaS marketing site — the closest thing in the catalogue to the product you are shopping for. ASoc Ledger (a finance-app site with live dashboards) and ASoc Magnet (a lead-capture site with channel insights) ship the same CSP-friendly structure: no third-party script hosts in the default policy, so adding your tracker is one reviewed line.

Browse the full sets: Next.js landing page templates, Tailwind landing page templates. For the other common pairing, see Vercel Analytics vs Google Analytics.

Keep reading

Comparison9 min read

Fathom vs. Vercel Analytics: Portability, Priced

Both are cookieless with a one-line install. The difference is what happens when you leave your host, measured against this site's live CSP, event wrapper and Lighthouse run.

Read more
Comparison12 min read

Firebase vs Convex: Where Authorization Lives

Rules in a DSL or logic in functions — judged on a shipped schema, including the check-then-act race that proved declarative rules can't hold an invariant.

Read more