Server-side tracking: what it is, what it fixes and what it can't

Server-side tracking records clicks and payments on your own server. What it fixes in Safari and ad blockers, what Cloud Run costs and a 6-step SaaS setup.

Muzahid Maruf — Founder of TrackRev.io

Muzahid MarufUpdated

Attribution · 8 min read
On this page
  1. 01How do browser tags and server records differ?
  2. 02Which four setups share the name?
  3. 03What stays broken after the move to a server?
  4. 04What does a server container cost?
  5. 05When is browser-side tracking enough?
  6. 06How do you set this up for a SaaS product?
  7. 07How does TrackRev record clicks on the server?

Explore with AI

Opens this article inside the chosen assistant with a ready-made prompt.

Server-side tracking means a server you control records the event, such as a click or a payment, instead of JavaScript in the visitor's browser.

The browser can still send the request, but the log entry, the cookie and any forwarding to Google Analytics 4, Google Ads or Meta happen on your infrastructure.

That removes several losses: blocked scripts, the 7-day limit Safari Intelligent Tracking Prevention puts on script-written cookies, and beacons that die with the tab. Consent and cross-device identity stay as they were.

Key takeaways

  • Safari caps JavaScript cookies at 7 days since ITP 2.1, or 1 day after link decoration since ITP 2.2, but a cookie set in an HTTP response keeps its expiry unless a third-party CNAME serves it, which Safari 14 caps at 7 days.
  • A Google Cloud Run server container costs about $45 per server a month with 2 recommended, while a redirect and a Stripe webhook run inside the app you already host.
  • EDPB Guidelines 2/2023 place tracking links and pixels under Article 5(3) of the ePrivacy Directive, so consent stays on the table even when a server does the recording.

How do browser tags and server records differ?

QuestionClient-side tagServer-side record
Where the code runsThe visitor's browserA machine you operate
How the cookie is setJavaScript, through document.cookieAn HTTP Set-Cookie header
Cookie lifetime in Safari7 days (ITP 2.1), or 1 day after link decoration (ITP 2.2)The expiry you set on your own domain, such as 365 days; 7 days when a third-party CNAME serves a subresource
Ad blockersDrop a listed script or beacon hostDrop only a listed tracking URL
Scroll depth and form fieldsVisibleInvisible
Stripe paymentSeen only if the buyer reaches your thank-you pageSeen through a webhook, retried for up to 3 days

Sources: WebKit and Stripe documentation.

A server-side record is an event that a machine you operate logs, and a client-side tag is JavaScript running in the visitor's browser.

The two differ in 3 ways: where the code runs, how long the cookie lasts and what each side can see. Cookie lifetime decides the most for a SaaS product.

Safari Intelligent Tracking Prevention (ITP 2.1, shipped in Safari 12.1 on February 21, 2019) caps a JavaScript cookie at 7 days, so a trial that pays on day 14 keeps its source only if something outlasted that cap.

A cookie set in a redirect response keeps the 365-day expiry you chose. The server never sees scroll depth or form fields, and the browser never sees the Stripe charge unless the buyer reaches the thank-you page.

Which four setups share the name?

SetupWhat the server recordsStill in the browserDocumented figures
Redirect click logThe click, plus the cookie in the redirect responseNothing until the landing page loadsA 301 may be reused from cache without reaching you, and a 302 may not (RFC 9110)
Server container (server-side Google Tag Manager)Events the container receives, forwarded to Google Analytics 4, Google Ads or MetaThe web tag that feeds the container, unless your own web server doesAbout $45 a month per Cloud Run server, 2 recommended (Google)
Conversion APIs (Meta Conversions API, GA4 Measurement Protocol)Conversions your backend sendsThe Meta Pixel or Google tag, for event IDs and client IDs, since Measurement Protocol is meant to augment gtag or Tag ManagerMeta deduplicates events arriving within 48 hours; Measurement Protocol takes 25 events and 130 kB per request, with timestamps up to 72 hours back (Google)
Payment webhook (Stripe)The charge, pushed to your endpoint, the same server-to-server pattern as an affiliate postback URLNothingRetries for up to 3 days in live mode; resend for 15 days in the Stripe Dashboard, 30 with the Stripe CLI (Stripe)

What stays broken after the move to a server?

Do server-set cookies still meet Safari's rules?

A cookie set in a server response escapes the 7-day script cap, but Safari applies other rules to servers, CNAMEs and link decoration.

Statcounter's September 2026 data puts Safari at 46.38% of US mobile usage, and 18.27% worldwide, so a Safari cap touches a large share of mobile traffic in the US.

DateReleaseRuleCookie your server sets in a response
Feb 21, 2019ITP 2.1, Safari 12.1, iOS 12.2Cookies created with document.cookie capped at 7 daysNot affected, since only document.cookie cookies are covered
Apr 24, 2019ITP 2.2, iOS 12.3document.cookie cookies capped at 1 day after link decoration from a classified domainNot affected
Mar 24, 2020Safari 13.1, iOS 13.4Third-party cookies blocked; script-writable storage deleted after 7 days of Safari use without interactionNot affected on your own domain
Nov 12, 2020Safari 14Cookies set in responses to third-party CNAME-cloaked subresource requests capped at 7 daysCapped when a subresource on your site, such as a tagging subdomain, CNAMEs to a third party

WebKit's November 2020 post cites a study that found 1,762 websites using CNAME cloaking for 56 trackers. WebKit's tracking prevention page adds that ITP detects third-party IP address cloaking too, with the same 7-day cap, and leaves that term undefined.

A CNAME to another subdomain of your site is not capped. Google's custom domain guide, last updated on June 27, 2025, says a server container left on its default provider domain can set JavaScript cookies only.

Stape's Cookie Keeper power-up, per its documentation, restores cookies that Safari 16.4 and later expire after 7 days.

Consent is required in many setups, because a server does not remove the legal question. The EDPB's Guidelines 2/2023 reached version 2.0 on 7 October 2024, after a consultation that ran from 16 November 2023 to 18 January 2024.

They treat tracking links and tracking pixels as falling under Article 5(3) of the ePrivacy Directive (2002/58/EC). Sending the link to the device counts as storage, and collecting the identifier counts as gaining access.

Being covered does not automatically require consent, since exemptions are assessed case by case. UK and US state rules differ, so take legal advice on your own setup.

TrackRev's pixel waits for grantConsent() when data-requires-consent is set, and in our setup the redirect itself runs no script.

What does a server container cost?

A Google Cloud Run server container costs about $45 per server a month, and Google recommends 2 servers, so the floor is $90 a month or $1,080 a year.

Stape Pro costs $17 a month on yearly billing for 500K requests, and Stape Free covers 10K requests.

OptionMonthly figureAnnual costCapacity
Cloud Run, 2 servers2 x $45 = $90$1,080Google's minimum recommendation
Cloud Run, 10 servers10 x $45 = $450$5,40035 to 350 requests per second across 2 to 10 servers
Stape Free$0$010K requests
Stape Pro$17$200500K requests
Stape Business$83$1,0005M requests
Stape Enterprise$167$2,00020M requests
Redirect and webhook in your appNo separate bill$0 extraYour existing hosting plan

Prices from each vendor's pricing page, October 2026. Google's figure comes from its setup guide, and Stape's monthly figures assume yearly billing.

Two Cloud Run servers cost $880 a year more than Stape Pro at $200 a year, per Stape's pricing page.

For source-to-payment attribution alone in our experience building TrackRev I would skip the container at first, since the redirect and the webhook cover the click and the charge, the events that decide the answer.

When is browser-side tracking enough?

Browser-side tracking is enough when the payment lands within 7 days of the click and no partner is paid from the result.

Safari 12.1 and later cap script-written cookies at 7 days, or 1 day after link decoration under ITP 2.2 (April 24, 2019).

For example, a trial that converts on day 14 needs a response cookie, because Google Analytics 4 files the charge under Direct.

Meta Ads and Google Ads optimization needs both sides: the tag captures the session and the Stripe backend sends the conversion.

SituationBrowser tag enough?Reason
Payment 1 to 7 days after the clickUsuallyA script cookie lasts 7 days, unless link decoration cuts it to 1
Payment 8 to 30 days after the clickNoThe cookie can expire first, and the charge lands under Direct
Partner commissionsNoA lost source pays the wrong partner, or none
Optimizing Meta or Google Ads on conversionsBothThe tag captures the session and your backend sends the conversion

How do you set this up for a SaaS product?

A logged click is a row your own server writes before the landing page loads, and it takes 2 of the 6 steps. First (step 1), put a redirect on a domain you own.

Share links like go.example.com/spring-launch, and tag every one, since an untagged click logs a blank channel. The route writes a click row with visitor ID, link, timestamp, user agent and referer, then answers 302.

Node.js crypto.randomUUID() returns a version 4 UUID for the visitor ID. A cache may reuse a 301 without asking again, so a repeat click behind a 301 can skip your server.

Second (step 2), send the visitor cookie on the redirect with SameSite=Lax, Secure and a Domain on your registrable domain, so subdomains can read it.

MDN's Set-Cookie reference says Lax also sends it on cross-site top-level navigations such as an email click, and Strict does not. Append the ID to the destination as vid and accept only UUID-shaped values on the landing page.

A redirect route in Express that logs the click and sets the cookie (cookie-parser assumed)
import { randomUUID } from "node:crypto"; app.get("/:slug", async (req, res) => {  const link = await db.links.findBySlug(req.params.slug);  if (!link) return res.redirect(302, "https://www.example.com");   const vid = req.cookies.vid ?? randomUUID();   await db.clicks.insert({    linkId: link.id,    visitorId: vid,    userAgent: req.get("user-agent"),    referer: req.get("referer"),    at: new Date(),  });   res.cookie("vid", vid, {    maxAge: 365 * 24 * 60 * 60 * 1000, // Express takes milliseconds    domain: ".example.com",    sameSite: "lax",    secure: true,    httpOnly: true,  });   const target = new URL(link.destination);  target.searchParams.set("vid", vid);  res.redirect(302, target.toString());});

How do you bind the ID to the signup and the payment?

A bound ID is a visitor ID saved on the user row and on the Stripe payment, and it takes 2 more of the 6 steps.

Third (step 3), read the ID in the request that creates the account and save it beside the email, since a script cookie may be gone when someone signs up 8 days after the click.

For example, a user who clicked on May 1 and signed up on May 9 still carries the May 1 visitor ID. A phone click and a laptop purchase share no ID until a login or email joins them.

Fourth (step 4), pass the ID to Stripe Checkout as client_reference_id or in metadata. Both fields come back on the Stripe Checkout Session in the checkout.session.completed event, the same event Stripe retries for up to 3 days.

The Stripe metadata attribution setup post has the field mapping.

How do you read charges and test the result in Safari?

Fifth (step 5), read charges through a Stripe webhook. Verify the Stripe-Signature header against the raw request body and return a 2xx before any heavy work. Stripe retries a failed delivery for up to 3 days in live mode.

Stripe's libraries reject a timestamp more than 5 minutes old by default.

Store each Stripe event ID, since the same event can arrive twice and out of order, and serve the endpoint over TLS 1.2 or 1.3, the only versions Stripe supports.

Sixth and finally (step 6), test in Safari. Click a tracked link in Safari 17 or later and read the cookie's expiry on the Storage tab of Safari Web Inspector.

A date 365 days out means the response cookie survived, and a date 7 days out means something capped it. The Safari ITP attribution post covers the remaining rules.

How does TrackRev record clicks on the server?

TrackRev is SaaS affiliate software that logs every click on our own redirect server, and we built TrackRev with link tracking and revenue attribution inside it.

A visit to a tracked link reaches TrackRev's redirect first, which logs the click, sets a 365-day visitor-ID cookie and forwards the visitor with a 302 response. A pixel on the landing page then reads the _vid parameter.

Revenue attribution starts at $29 a month on Indie, billed monthly for 1 site. Payments sync from Stripe, Paddle Billing, Polar, Lemon Squeezy, Creem and Dodo Payments. A team that needs only a click log can use a standalone tracker.

StepWhat TrackRev doesDetail
ClickLogs each click at its own redirect before your page loads302 response with a visitor-ID cookie (Lax, Secure, 365 days); UTM parameters and _vid added to the destination. See first-party tracking
RecordTimestamp, IP address, location, device, browser, OS, referrer, bot flagCrawler and empty user agents and prefetches flagged; click.created webhook for humans only. See bot filtering
IdentityA pixel reads _vid and writes its own JavaScript cookieSafari's 7-day cap applies to that copy; window.trk.identify binds the email on the server
PaymentStripe read hourly with a read-only restricted keyOptional webhook; match order is metadata.vid, client_reference_id, email. See revenue attribution
ConsentPixel waits for grantConsent() when data-requires-consent is setThe redirect itself runs no script
PlansFree: 50 links and 1,000 tracked events a monthRevenue attribution starts at $29 a month on Indie, per the pricing page
Not includedA tag manager, or forwarding to ad platformsA server container or the Meta Conversions API does that job

Found this useful? Share it.

PostLinkedIn

Frequently asked questions

Muzahid Maruf — Founder of TrackRev.io

Written by

Muzahid Maruf

Founder, TrackRev.io & Contant.io

Muzahid Maruf founded TrackRev.io, SaaS affiliate software with no limit on tracked revenue, and Contant.io. He writes about affiliate programs.

Writes about Marketing attribution · Link tracking · Revenue analytics · SaaS growth

Run your affiliate program without a revenue cap.

Affiliate software for indie hackers and small SaaS teams: $29 a month flat, 0% fees, link tracking and revenue attribution built in. Free to start, no card.