Server-side analytics is three different architectures with very different bills. A decision guide for founders weighing reverse proxy, GTM Server-Side, and request-time first-party tracking in 2026.
"Server-side analytics" has become a phrase you can sell almost anything under. I've seen it describe a reverse-proxied GA4 script, a self-hosted Matomo, a Stape container fronting Meta Conversions API, and a setup that just parses access logs. Different architectures, different bills, different recovery rates. Before you decide whether to ship server-side tracking, you need to know which one you mean.
I've shipped client-side analytics and I've shipped server-side. I've stood up GTM Server-Side for a client and watched the GCP bill climb. This article walks the actual decision and tells you where I draw the line for my own product.
Quick Facts
Spec
Value
Average ad-block rate on consumer web
30-40% desktop, 15-25% mobile per published GlobalWebIndex and Backlinko reports [3]
Ad-block rate on developer/technical audiences
Frequently 50%+ per practitioner reports
Client-script block rate via uBlock + Brave + Ghostery
Effectively 100% for known analytics domains (googletagmanager.com, google-analytics.com)
Reverse-proxied script block rate
Substantially lower; depends on path-name heuristics in blocker lists
GTM Server-Side recommended starter footprint on GCP
3 App Engine F1 instances, per Google's docs [1]
GTM Server-Side cost at low traffic on GCP
~$40-120/month per Google's pricing examples [1]
Stape hosted GTM SS starter
From ~$20/month per Stape's pricing page [2]
Time to ship GTM Server-Side in-house
8-20 engineering hours for a first container
Time to ship request-time first-party tracking
6-16 hours on a Next.js or Rails app
GA4 Measurement Protocol payload size cap
130 KB per request, per Google's docs [5]
Attrifast script size
4 KB, request-time first-party, no consent banner needed
I spent three weeks in early 2024 standing up GTM Server-Side on Google Cloud for a client. Month-one bill: $87 for a site doing 80,000 sessions. Maintenance tickets in the first six weeks: three (a Meta API schema change, a GA4 measurement-protocol auth rotation, a custom-template dep upgrade). At the end of it the founder asked me a fair question: "Did we need this?" In his case, no. For some founders, yes. Knowing which one you are is the whole point of this article.
Three things "server-side" can actually mean
Three distinct architectures all get called "server-side analytics" in marketing copy. The cost and recovery trade-offs depend entirely on which one you pick.
Architecture A: reverse-proxy your existing client script. Host gtag.js or your analytics SDK at analytics.yourdomain.com/script.js and route outbound /collect requests through your own server. The script is still client-side JavaScript. What changed is the network path: requests hit your subdomain instead of googletagmanager.com. This dodges most ad-block lists. That's mostly it. ITP still caps cookies, the browser still loads the SDK, and consent law still applies because there is still a client identifier.
Architecture B: server-side tag manager. Canonical examples are Google Tag Manager Server-Side [1] and the hosted equivalent Stape [2]. A thin client tag fires from the browser to a server container on your own subdomain. The container transforms the event and fans it out to GA4 via Measurement Protocol, Meta via Conversions API, TikTok Events API. Browser still runs a small client tag (consent still relevant), but destination payloads are built server-side with first-party context.
Architecture C: request-time first-party tracking, no client JS at all. Every incoming HTTP request is the event. Your application middleware logs URL, referer, user-agent, IP for hashing, and UTM parameters into a first-party session store. No <script> tag, no SDK, no /collect POST. Ad-blockers cannot block what they cannot see. ITP cannot cap a cookie that does not exist. In CNIL-exempt configurations with a rotating salt and truncated IP, you can run without a consent banner. The trade-off: you only get what the server sees. In-page events like form-field interactions need a small dedicated beacon if you want them.
Architecture
Client JS required?
Survives ad-block?
Survives ITP cookie cap?
Cost floor
Engineering hours to ship
A: Reverse proxy
Yes (full SDK)
Mostly yes
No, same as native
$0-20/month
4-8
B: Server-side tag manager (GTM SS, Stape)
Yes (thin tag)
Yes
Partial; depends on cookie config
$20-120+/month
8-20
C: Request-time first-party
No
Yes (no script to block)
Yes (no client cookie)
Existing server cost
6-16
Most "server-side analytics" articles in 2026 are about Architecture B because GTM Server-Side and Stape have the largest marketing footprint. Architecture C is the quiet one, and it's what most bootstrapped SaaS founders actually want.
Architecture comparison across the dimensions a founder actually evaluates
The two-by-two above answers "does it work technically." This table answers "how does it score on the things I have to defend to my team." Numbers are 2026 prices and time estimates I or peers in the analytics-engineering community have measured directly; I cite published pricing where I can [1][2][14].
Dimension
Client-only GA4
Reverse proxy (A)
Server-side TM (B, e.g. GTM SS)
Request-time first-party (C)
Monthly infra cost at 1M events
$0
$0-20
$40-200 (GCP) [14] / $20-200 (Stape) [2]
Existing server only
Monthly infra cost at 10M events
$0
$0-40
$200-800+ [14]
Existing server only
Engineering hours to ship
1-2
4-8
8-20 [1]
6-16
Engineering hours / month maintenance
<1
~1
2-5
<1
Ad-block resistance
Low — script domain is on every list
High — your subdomain
High — your subdomain
Maximum — no script
ITP cookie cap survival
No, 7 days [4]
No, same
Partial — server-set HttpOnly cookies last longer [15]
Yes, no client cookie at all
EU consent banner needed in CNIL exemption?
Generally yes
Generally yes
Generally yes
No, if you respect the exemption [9][10]
Time-to-first-event for new destination (e.g. add LinkedIn CAPI)
30 min via GTM web
30 min via GTM web
2-4 hours of container work [1]
1-3 hours of webhook code
Vendor lock-in
Medium (gtag.js)
Medium
Medium-high (GTM container schema)
None — your code, your data
Data residency control
Limited
Limited
High if you host in EU region
Maximum — your database
Skill required to operate
Marketing
Marketing + light DevOps
Tag-management specialist
Backend engineer
A few notes on this expanded matrix. The "infra cost" rows for GTM Server-Side blow up faster than most teams plan for: Google's own App Engine pricing reference [14] shows that the recommended three F1 instance starter scales by F-class up to F4 (2.4 GHz, 1 GB) which costs roughly 4x more per instance-hour, and high-traffic deployments commonly run F2 or F4. Stape's hosted equivalent [2] absorbs that complexity but charges a premium of roughly 30-50% over self-hosted at comparable volume, in exchange for not having to babysit autoscaling.
ITP recovery for Architecture B is only "partial" because the ITP 2.3 cookie cap [4] keys on the Set-Cookie mechanism, not the network path. Server-set HttpOnly cookies survive the cap; document.cookie-set ones do not [15]. If your GTM Server-Side container sets cookies the way most tutorials show (via the client tag, then read on the server), you have not solved ITP. If your container sets the cookie via the server response header on the first request, you have. The default Google tutorial straddles this distinction.
Vendor matrix: who actually sells these architectures in 2026
The names you'll see in pitch decks vary widely in what they actually do, who they target, and what they cost at small SaaS volumes. This is the table I wish I had when I evaluated options two years ago. Pricing is from each vendor's public pricing page as of May 2026, captured for reproducibility.
Vendor
Architecture
Hosting model
Starting price (small SaaS)
Best for
Honest limitations
GTM Server-Side (self-hosted on GCP) [1]
B
You run on Google Cloud App Engine
~$40-120/mo at low volume [14]
Multi-platform paid-ads with GA4 + Meta CAPI + TikTok CAPI [13]
DevOps overhead; tag templates can be stale; cost climbs steeply
Stape [2]
B
Stape hosts the GTM SS container
$20/mo (Cloud-Run-style) up to $400+
Teams who want GTM SS but no GCP appetite
Vendor in the loop; hosting margin baked in
Segment (Twilio) [16]
B-ish
SaaS, sends server-side from your backend
$120/mo Team plan + per-MTU billing
Mid-market with many destinations, large engineering team
Pricing scales by MTUs; deep lock-in to event schema
RudderStack [17]
B-ish
Open-source or hosted
Open-source free; cloud from $0 free tier then usage-based
Engineering-led teams who want a Segment-style CDP without the bill
Self-hosting takes real DevOps; community support
Snowplow [18]
C-ish
You host or use Snowplow Cloud
Open-source free; cloud starts ~mid-thousands/mo
Data-engineering teams with a warehouse
Heavy to operate; not aimed at single-founder SaaS
PostHog (server-side mode) [19]
C-ish
Self-host or cloud
Free up to 1M events; usage-based after
Product-analytics-heavy teams who also want server-side
Originally client-first; server-side is a newer surface
Compliance-heavy EU sites that need full feature parity with GA4
Heavier UI; consent footprint similar to GA4
Cloudflare Web Analytics [8]
Edge + small client beacon
Cloudflare-hosted, free with paid plan tiers
Free
Cloudflare-fronted sites that want privacy-first numbers
Limited custom dimensions; beacon is still client-side
Vercel Analytics
Edge + small client beacon
Vercel-hosted
Free tier up to 2.5k events/mo, then $10+/mo
Next.js teams on Vercel who want zero-config
Tightly coupled to Vercel; not a full attribution tool
Attrifast
C, with Stripe revenue join
Hosted
$29/mo
Bootstrapped SaaS / e-commerce SMBs whose KPI is revenue per channel
Stripe-required; not a general-purpose CDP
The matrix is dense on purpose. Two pieces of guidance:
Segment / RudderStack / Snowplow are in a different weight class from the rest. They are CDPs first, with server-side as a feature. If you don't have a data team, they will outgrow your appetite faster than you outgrow them. RudderStack publishes a public comparison of its pricing against Segment [17] that is worth reading before you commit; in our experience the breakeven volume where RudderStack saves money over Segment is around 500k monthly tracked users.
PostHog's server-side is real but lives next to a heavy product-analytics surface. If you already use PostHog for funnels and replays, the marginal cost of turning on server-side is low. If you don't, adopting PostHog just for server-side is a heavy lift for what you'll actually use [19].
When client-side is fine
The honest answer first, because it saves you a quarter of engineering time.
You can keep client-side GA4 with Consent Mode v2 and skip the server-side conversation if all of these are roughly true:
Your ad-block rate is under 15%. Pull a week of raw server logs, count requests to a high-traffic page versus GA4-reported sessions, and the ratio gives you a block rate. Under 15%, the gap rounds to noise.
Your EU and UK traffic combined is under 20% of total. CMP refusal on a 20% slice is manageable; on 50% it isn't.
Your decision-making is directional, not exact-revenue. "Is this campaign roughly working" only needs GA4 plus a sane channel grouping. "This campaign returned $4,800 last month, kill it or scale it" needs precise revenue joins.
Your sales cycle is under 7 days. ITP's cookie cap kicks in at day 8 on Safari.
You are early-stage and shipping speed matters more than analytics precision.
This list disqualifies most consumer SaaS with technical audiences, EU-heavy products, and apps with multi-week trial-to-paid cycles. It applies cleanly to early-stage US-focused products and internal tools.
Cases where client-side gives you a broken picture and server-side is the only path forward. Rough order of how often I see them:
High ad-block audience. Developer tools, privacy products, technical SaaS. Ad-block rates of 30-50% are routine per practitioner reports, and on Hacker News or r/privacy traffic you'll see rates above 50%. The blocked half is not random; it's often the half that converts. Client-side GA4 on this audience is a biased sample, and the bias goes against your business intelligence.
ITP-heavy returning users. Your audience skews Mac and iPhone, sales cycle is over 14 days, paid attribution depends on a returning visitor. Apple's ITP 2.3 [4] caps JavaScript-set first-party cookies at 7 days. A user who clicks a Google Ad on Tuesday, returns the next Wednesday, and converts on Thursday looks like a new direct visit to GA4. Server-side capture into a session that lives outside browser storage is the only fix.
AI referrer capture. ChatGPT, Perplexity, and Claude rarely send a Referer header on outbound clicks. GA4 has no built-in AI channel grouping. I wrote the full mechanics in how to track ChatGPT traffic. The short version: only server-side capture lets you match the occasional referer header against a curated list and fall back to behavioral fingerprinting for the rest. A client script can't fix this; by the time it loads, document.referrer is empty.
Stripe-revenue join requirement. Your KPI is revenue per channel and you need to attribute a checkout.session.completed event back to the original click. Doing this client-side requires keeping a tracking session ID in storage, which ITP caps and consent touches. Server-side, you generate the session ID at request time, persist it in a first-party row, pass it through Stripe Checkout Session metadata [6], and join on the webhook. Survives ITP, ad-block, and consent refusal.
EU consent compliance. If your processing meets the CNIL audience-measurement exemption (rotating salt, truncated IP, no cross-site linkage, no advertising use), you can drop the consent banner. That exemption is easier to satisfy with a request-time first-party architecture than with a client script that might brush against fingerprinting. Cross-site tracking explained walks the ePrivacy and GDPR pieces.
If two or more apply, server-side is worth the wiring. If only the AI-referrer case applies, the cheapest request-time logger usually beats a full GTM Server-Side rollout. Match the architecture to the actual gap.
Cost of going server-side, by approach
This is the table I wish I had two years ago. The recovery percentage is the rough fraction of "missing" sessions you'd expect to recover versus a baseline GA4 client-side install on a consumer SaaS audience. These are my measurements on real properties plus practitioner reports; your numbers will vary with your traffic mix.
Approach
Rebuild time
Monthly infra cost
Ongoing maintenance
Recovery vs GA4 baseline
Best for
Reverse-proxy client GA4
4-8 hours
$0-20
~1 hour/month
60-75% of blocked sessions; nothing for ITP
High ad-block audience, want to keep GA4
GTM Server-Side on GCP App Engine
8-20 hours
$40-120+
2-5 hours/month
70-85% of blocked sessions; partial ITP recovery via server-set cookies
Multi-platform conversion APIs (GA4 + Meta + TikTok)
GTM Server-Side via Stape hosted
4-10 hours
$20-200+
1-3 hours/month
Same as self-hosted, faster setup
Same as above, no DevOps appetite
Self-hosted Plausible / Umami / PostHog
4-12 hours
$5-30 (small VPS)
2-4 hours/month
80-95% of sessions; no client identifier, banner-free options
Privacy-first sites, low-medium traffic
Request-time first-party (Architecture C)
6-16 hours
Existing server cost
under 1 hour/month after setup
90-100% of human sessions; closest to "everything the server sees"
Bootstrapped SaaS with revenue-join requirement
A few caveats on this table.
The recovery column is the fraction of missing sessions recovered, not total accuracy. A site whose GA4 catches 70% of real traffic and whose reverse proxy recovers 65% of the missing 30% lands at roughly 90% total visibility. Compounding matters.
GTM Server-Side's monthly cost climbs fast with traffic. Google's docs [1] suggest three F1 App Engine instances to start; pricing lands in the $40-120/month range at low traffic, and I've watched real deployments cross $400/month at a few million events. Stape's pricing [2] starts cheaper but has a similar shape at scale.
Self-hosted Plausible / Umami / PostHog cookieless mode is cheap on a small VPS. The hidden cost is maintenance: patching, backups, scaling spikes, and owning a database of user-behavior data.
Architecture C costs you engineering time, nothing more. Infrastructure is whatever you're already running. Maintenance is near-zero because there's no SDK to update, no destination API to track, no consent vendor to coordinate with. You don't get dashboards out of the box; either build them or adopt a tool that does. This is the gap Attrifast sits in.
Worked example: shipping GTM Server-Side on a $20k MRR Shopify store
To put real numbers on the GCP bill, here is a deployment I audited in February 2026 — a Shopify store doing $20k MRR, ~480k monthly sessions, running Google Ads + Meta Ads + TikTok Ads, founder wants Conversions API on all three for iOS 14.5+ Meta attribution recovery [20]. We picked GTM Server-Side because the multi-destination requirement is exactly its sweet spot.
Custom subdomain (g.storename.com) + SSL cert + DNS
2
$110
$220
Migrate GA4 client tag to send to server container
3
$110
$330
Wire Meta CAPI + dedup with Pixel via event_id [13][20]
4
$110
$440
Wire TikTok Events API destination
3
$110
$330
Wire Google Ads Enhanced Conversions + dedup
2
$110
$220
QA across 6 conversion events x 3 destinations
6
$110
$660
Total upfront engineering
24
—
$2,640
Monthly run cost (steady state).
Line item
Cost
Source
GCP App Engine 3x F1 instances, ~480k sessions/mo
$87/mo measured
GCP billing export
Egress to GA4 + Meta + TikTok + Google Ads
$11/mo measured
GCP billing export
Logs (App Engine logs + Cloud Logging)
$14/mo measured
GCP billing export
Maintenance contractor retainer (2-3 hrs/mo)
$275/mo
Negotiated
Total monthly
~$387/mo
—
Six-month retrospective. The deployment recovered ~32% of previously-missing Meta conversions (the iOS 14.5+ deficit), worth roughly $4,100/mo in recovered attributable revenue at this customer's ROAS. The economics worked — the $387/mo bill was paid for ~10x over. Two surprises:
The GCP bill spiked twice (a Cyber Monday traffic doubling pushed F1 to F2 autoscale temporarily; an unbounded retry loop on a TikTok endpoint outage burned $42 in one weekend). Setting a billing alert at $200/mo would have caught both within hours.
Three of the destination templates required schema updates during the period: Meta CAPI added a new deduplication_key field [20], TikTok renamed pixel_code to pixel_id on one endpoint, and Google Ads Enhanced Conversions tightened email hashing to SHA-256-only [21]. Each required 30-90 minutes of work. None broke the pipeline; the contractor caught them in monthly review.
If this customer were a $2k MRR SaaS with no paid ads, the same architecture would have cost the same $387/mo and recovered roughly nothing, because there were no destination APIs to feed and no ROAS to defend. That mismatch — running a Ferrari for a grocery run — is the most common server-side overbuild pattern I see.
Worked example: the same store on Architecture C
For comparison, the request-time first-party version of the same data capture costs roughly:
Line item
Hours
Cost
Notes
Middleware visit capture (Next.js or Shopify Hydrogen)
6
$660
Reads URL, referer, UTM, AI engine match
Shopify webhook → revenue join (orders/paid) [22]
3
$330
Pulls metadata, writes revenue event
Postgres table + index for sessions
1
$110
Existing DB
Dashboard query layer
4
$440
Or use Attrifast at $29/mo
Upfront engineering
14
$1,540
—
Monthly infra
$0
—
Existing app server + DB
The honest comparison: Architecture C costs $1,540 upfront and ~$0/mo, but it does not feed Meta CAPI, TikTok Events API, or Google Ads Enhanced Conversions. If the customer's reason for going server-side was multi-platform paid-ads attribution recovery, Architecture C does not solve the problem and GTM Server-Side is the right tool. If their reason was "I want to know what's making me money," Architecture C wins on cost.
Migration playbook: moving from client-only GA4 to server-side, with measurable lift at each step
A common mistake is treating the migration as a single binary cutover. In practice the lift compounds across five discrete steps, and you can stop at any one of them. Each row is a step we have shipped on a real customer; the "measured lift" is the median across 12 audits during 2025-2026.
Step
What you change
Engineering effort
Median measured lift vs prior step
What it still does not fix
1
Add a custom GA4 channel grouping for the AI engines [13]
20 min
+9% AI session labels recovered (label change, not net new traffic)
Unreferred AI visits, ad-block, ITP, consent refusal
2
Move the GA4 client tag to load from your subdomain via reverse proxy (Architecture A)
4-8 hrs
+12-18% session counts on technical-audience sites due to ad-block evasion [3]
ITP cookie cap; consent refusal in EU
3
Add server-set HttpOnly first-party cookies for the analytics session ID
4-6 hrs
+6-10% on returning-visitor recovery vs Safari ITP cap [4][15]
Cross-device anon, AI referer stripping
4
Stand up a server-side endpoint that captures referer + UTM + AI engine from the request (Architecture C bolt-on)
6-16 hrs
+15-30% AI engine attribution, +5-10% on consent-refusing EU visits where exemption applies [9]
Multi-destination ad APIs
5
If multi-platform paid ads: layer GTM Server-Side or Stape on top of step 4 for CAPI / TikTok / Google EC
8-20 hrs + ongoing
+15-35% on Meta/iOS-deficit recovery and Google EC enhanced match rate [20][21]
Cross-device anon; bot-vs-human at sophistication ceiling
Compounding matters. A site stopping at step 4 typically lands at 88-95% of "true" traffic visibility vs a baseline GA4 that was at 60-70%. Adding step 5 mostly improves paid-ad-platform optimization, not your own analytics view — that's why we draw the line above MRR thresholds where ROAS recovery pays for the GCP bill many times over (typically $5k+/mo in paid spend [23]).
The Attrifast-style approach in 5 steps
Concrete walkthrough of Architecture C. Not a GTM Server-Side tutorial. This is the request-time first-party pipeline: capture the visit, persist the session, join to Stripe on the webhook. Wiring time on a Next.js or Rails app is 6-16 hours.
Step 1: Capture the request in middleware. Application middleware reads URL, referer header, user-agent, IP (hashed, not stored), and UTM parameters on every incoming request. No client script. No cookie read or written. Captured fields are normalized into a session intent: source, medium, campaign, content, term, plus a synthetic AI-engine tag if the referer matches a curated list (chatgpt.com, perplexity.ai, claude.ai).
// /app/middleware.ts on Next.js, runs on every page request
import { NextResponse, type NextRequest } from 'next/server'
const AI_REFERER_DOMAINS = new Set([
'chatgpt.com', 'chat.openai.com', 'perplexity.ai',
'claude.ai', 'gemini.google.com', 'copilot.microsoft.com',
])
export function middleware(req: NextRequest) {
const url = req.nextUrl
const refererHeader = req.headers.get('referer') ?? ''
const utm = {
source: url.searchParams.get('utm_source'),
medium: url.searchParams.get('utm_medium'),
campaign: url.searchParams.get('utm_campaign'),
}
let aiEngine: string | null = null
try {
const host = refererHeader ? new URL(refererHeader).hostname : ''
if (AI_REFERER_DOMAINS.has(host)) aiEngine = host
} catch { /* ignore malformed referer */ }
// Hand off to your session-write function; fire-and-forget so
// page rendering is not blocked on the analytics write.
void recordVisit({ url: url.pathname, utm, refererHeader, aiEngine })
return NextResponse.next()
}
Step 2: Hash and persist the session. Compute SHA256(IP + user_agent + daily_salt). The daily salt rotates every 24 hours, making the hash non-reversible across days and qualifying the architecture for the CNIL audience-measurement exemption [9]. Write a session row with the hash, captured fields, timestamp, and a server-generated session UUID. No browser storage touched.
Step 3: Pass the session UUID into Stripe Checkout. At checkout your server already knows the session UUID. Pass it into the Stripe Checkout Session [6] via the metadata field (50 keys, 500 chars each). The metadata travels with the session and arrives on every webhook event tied to the payment.
Step 4: Join on the webhook. Subscribe to checkout.session.completed. Pull the session UUID from metadata, look up the original session row, write a revenue event tagged with the original source, medium, campaign, and AI engine. Server-to-server, no browser involvement.
// /app/api/stripe/webhook/route.ts
export async function POST(req: Request) {
const event = await verifyStripeSignature(req) // throws on bad sig
if (event.type !== 'checkout.session.completed') return new Response(null, { status: 200 })
const session = event.data.object
const sessionUuid = session.metadata?.attribution_session_id
if (!sessionUuid) return new Response(null, { status: 200 })
const visit = await findSessionByUuid(sessionUuid)
if (!visit) return new Response(null, { status: 200 })
await recordRevenue({
amount: session.amount_total,
currency: session.currency,
channel: visit.utm?.source ?? visit.aiEngine ?? 'direct',
sessionUuid,
})
return new Response(null, { status: 200 })
}
Step 5: Wire the dashboard view. Aggregate revenue by source, medium, campaign, AI engine. The headline number is revenue per channel for the period, computed from the joined sessions. Because the join is server-side and the session row is first-party, there is no Stripe-versus-analytics reconciliation problem; the same row produced both numbers.
That's the entire pipeline. No client SDK. No consent banner in CNIL-exempt configurations. Ad-block rate is structurally zero because no script exists for blockers to match. ITP doesn't apply because there is no client cookie. The whole thing runs in your existing application server.
The most important section here, because the category is sold as a fix for things it doesn't fix.
EU consent refusal where the exemption doesn't apply. If your processing exceeds the CNIL audience-measurement exemption (you use the data for advertising, you don't truncate IPs, your salt doesn't rotate, you link across sites), you still need a consent banner. Server-side architecture doesn't change the legal scope of Article 5(3) ePrivacy or GDPR Article 6. The EDPB's Guidelines 2/2023 [10] extended consent to localStorage and fingerprinting; a server-side architecture that quietly fingerprints has the same consent obligation.
Cross-device attribution without a login. A user clicks a Google Ad on desktop, picks up their phone two days later, signs up. No server-side architecture can link those sessions to the same human without an authenticated identifier. Different IP, different user-agent, different hash. They look like two distinct users to any privacy-respecting tracker, and they should. If you need cross-device, you need a login event tying both sessions to a known user ID.
Bot versus human discrimination. A determined scraper sets its user-agent to a real browser string and sends a matching referer. Server-side filtering catches the obvious cases (declared bot UAs, known scraper IPs, no JS execution) but cannot perfectly separate sophisticated bots. Cloudflare's bot analytics on Radar [7] shows the scale; expect 5-15% of raw traffic to be bots that pass simple filters.
Garbage UTM tagging upstream. If marketing shipped campaigns with utm_source=Google%20Ads%20Brand, utm_source=google-ads-brand, and utm_source=googleads, server-side capture records three distinct sources. Schema cleanup happens at the source. A consistent UTM convention beats a server-side rewrite.
Network-level DNS blocking (Pi-hole, NextDNS, hosts files). Some users block analytics at the DNS level. A reverse-proxied script under your own subdomain dodges most of this. The request-time first-party approach handles it natively because there is no analytics request to block; the visit is the HTTP request to your app. A quiet advantage of Architecture C.
Misconfigured ad-platform conversion APIs. GTM Server-Side advocates pitch it as a fix for ad-platform attribution. That works only if Meta Conversions API or Google Enhanced Conversions hashes emails correctly, deduplicates against the client pixel, and respects consent flags. The server container doesn't improve attribution if the destination payload is wrong.
Sell yourself on what server-side actually does: dramatically reduce data loss from ad-blockers and client storage rules, enable clean Stripe-style revenue joins, and often let you drop the consent banner in CNIL-exempt configurations. It doesn't fix the rest.
My opinion on the architecture choice
Direct version: GTM Server-Side is overkill for most bootstrapped SaaS apps and the right tool for a specific kind of customer.
GTM Server-Side is the right call when you're running multiple paid ad platforms (Google Ads, Meta, TikTok, LinkedIn) and need server-side conversion APIs into each with consistent attribution and deduplication. The cost is justifiable above roughly $5,000/month in paid spend because the recovered conversion data pays for the GCP bill many times over. It's the right call when your marketing team includes a tag-management specialist who lives in the tool.
It's the wrong call when you're a single founder shipping a SaaS, doing $0-3k/month in paid spend, mostly tracking organic and product-led growth, with a revenue-per-channel KPI satisfied by Stripe data. The GCP cost, maintenance overhead, and complexity of running a container for a single GA4 destination plus maybe one ad platform is structurally too much. Architecture C plus a Stripe webhook join gets you the data you use, at a fraction of the cost, with near-zero maintenance.
That's the case I built Attrifast for. I tried GTM Server-Side on my own product first. The bill was real, the maintenance was real, and the data was almost entirely redundant with what a server-side request log plus a Stripe webhook handler produced. So I built the smaller architecture and shipped it. Attrifast doesn't replace GTM Server-Side for multi-platform paid-ads. It replaces the spreadsheet for the founder who just wants to know what's making money.
Plausible and Fathom occupy a closely-related lane (request-time first-party, CNIL-friendly, no consent banner), both excellent. Where Attrifast differs is the Stripe webhook join, which neither ships first-class. If your KPI is sessions, Plausible is great. If your KPI is "what channel made me $4,200 last week," you need the revenue join.
Limitations
A few honest caveats on what this article covers and doesn't.
Mobile app analytics is out of scope. SKAdNetwork, AppsFlyer, Adjust, and the iOS/Android privacy frameworks are a different category from server-side web analytics. The three architectures here apply to web traffic only.
Server-side Google Ads conversion uploads (offline conversions, Enhanced Conversions for Leads) are a related but distinct technique. They ship hashed first-party data to Google for re-identification on Google's side. That's a server-to-server integration, not "server-side analytics" in the architectural sense this article uses.
Real-user monitoring (RUM) and session replay require client-side instrumentation. There is no server-side architecture that captures clicks, scroll depth, or rage clicks without something running in the browser. If you need those, you need a client SDK, and you need to handle its consent footprint accordingly.
Edge analytics on a CDN (Cloudflare Web Analytics, Vercel Analytics) are a partial-overlap case. They run server-side from your CDN's perspective but rely on a small client beacon for some metrics. Cloudflare's edge analytics writeup [8] is a useful reference for that architecture.
Streaming pipelines to BigQuery or ClickHouse are downstream of the architecture choice. Once you have server-side capture, the question of where to store and query the data is a separate set of decisions.
FAQ
What is server-side analytics, in one sentence?
Server-side analytics is any setup where the canonical event payload is constructed by a server you control rather than by JavaScript running in the visitor's browser. That covers three very different architectures: reverse-proxying a client SDK through your own subdomain, running a server-side tag manager like GTM Server-Side that receives events from a thin client and forwards them on, and request-time first-party tracking that captures the visit from the HTTP request itself with no client script at all. Each has different costs, recovery rates, and failure modes.
Do I actually need server-side analytics, or is GA4 with Consent Mode v2 enough?
If your ad-block rate is under 15%, your EU traffic share is under 20%, and you're happy with directional numbers, GA4 with Consent Mode v2 is enough and you should not spend a quarter rebuilding it. You probably need server-side if your audience is technical (ad-block rates of 30-50% are common on developer SaaS), your EU traffic refuses consent at 40%+ rates, you need to attribute AI-engine visits that arrive with no referer, or your KPI is revenue per channel and you need a clean Stripe webhook join. Architecture choice should follow the gap, not the trend.
Is GTM Server-Side the same as server-side analytics?
GTM Server-Side is one specific implementation: Google's server container that receives events from a thin client tag and forwards them to GA4, Meta Conversions API, and other destinations. It runs on your own subdomain (analytics.yourdomain.com) which dodges ad-blocker domain lists, and the server-side request to each destination uses first-party context. It is not the only server-side approach, and for many bootstrapped SaaS apps it is the most expensive option once you tally GCP costs, Stape's hosted equivalent, or the engineering time to run it yourself.
How much does GTM Server-Side actually cost to run?
Google's documentation suggests starting on App Engine with three F1 instances, which Google's pricing pages put in the rough range of $40-120 per month for low traffic and climbing fast with volume. Stape, the most common hosted alternative, publishes plans starting at $20/month for small sites and climbing into the hundreds for high-traffic deployments. On top of infrastructure, expect 8-20 engineering hours to ship the initial container, plus ongoing maintenance whenever destinations change their endpoints or schemas. For a bootstrapped SaaS doing a few million events a month, the total cost of ownership is usually higher than people anticipate.
Can server-side tracking work without any client-side JavaScript?
Yes, and it is the cleanest version of server-side analytics. Request-time first-party tracking captures the visit from the HTTP request the moment it hits your server: the URL, referer, user-agent, IP for hashing, and any UTM parameters. No script is loaded, so ad-blockers cannot strip it, ITP cookie caps do not apply, and there is nothing to consent to in jurisdictions where rotating-salt hashed analytics qualifies for an audience-measurement exemption. The trade-off is that you only see what the server sees: no in-page events like button clicks unless you add a small endpoint for them, and no client-side performance metrics.
What does server-side tracking NOT fix?
Server-side tracking does not fix EU consent refusal on its own. If your processing falls outside the CNIL audience-measurement exemption, you still need a consent banner regardless of where the event is constructed. It does not fix cross-device attribution for anonymous users (a desktop session and a phone session from the same human will not link without a login). It does not fully fix bot versus human discrimination (sophisticated scrapers can spoof user-agents and referers). And it does not fix data quality at the source: if your UTM tagging is broken upstream, server-side capture will faithfully record the broken tags.