Safari, cookies and server-side tracking on Shopify
Safari's tracking prevention shortens how long browser-set cookies last, which blurs attribution. Here is what server-side tracking and Shopify's customer events can and can't recover, within…

- Published
- 22 Oct 2026
- As of
- 21 Oct 2026
If your Safari numbers look thinner than the rest of your traffic, tracking prevention is a likely reason. This guide is for developers and marketers on Shopify. It explains what Safari restricts, how that distorts attribution, what Shopify's customer events and server-side setups can realistically do, and how to stay inside consent rules.
What Safari actually restricts
Safari's Intelligent Tracking Prevention (ITP) is WebKit's set of default privacy rules, and it blocks all third-party cookies. WebKit says there are no exceptions. Access can only be granted through the Storage Access API and a temporary compatibility fix for popups. MDN's guide to third-party cookies makes the same point: in Safari you can switch off cross-site tracking prevention, but per-frame access is a code-level job. A related rule is that a third party can't set new cookies unless it already has some, so a domain has to become first-party before it can set its initial ones.
The rules that hurt attribution most hit first-party cookies written in JavaScript. When ITP detects link decoration (tracking parameters added to a URL), it caps the expiry of cookies created in JavaScript on the landing page to 24 hours. Separately, it deletes all cookies created in JavaScript, and all other script-writeable storage, after 7 days. Classified domains get a harsher rule: all their website data is deleted if they haven't had user interaction as first-party, or a storage access grant, in the last 30 days of browser use. Scrolling doesn't count as interaction. A click, tap or keyboard entry does.
Referrers change too. All third-party referrers are downgraded to their origins by default, in both the HTTP referrer header and document.referrer. So the full landing path you might have relied on to tell campaigns apart can disappear. In practice this tends to mean a returning shopper looks like a new visitor, and the original source gets lost.
Here's the practical tip: audit which of your tags set cookies in JavaScript. Open Safari's developer tools on a test store, load a page, and note which cookies are written by scripts rather than by a server response header. Those are the ones on the short clock. Then check any cross-site cookies. MDN suggests searching your code for SameSite=None, or looking for stored SameSite=None cookies in DevTools, to find them. Once you have the list, you know which tags need a different approach.
What that does to attribution
A returning visitor can look brand new because of how Safari treats cookies written by JavaScript. Most analytics tags set their visitor ID that way, and WebKit's tracking prevention documentation says it deletes all cookies created in JavaScript, and all other script-writeable storage, after 7 days. Someone who browsed on Monday and comes back to buy the following week may arrive with no ID at all. Your analytics then counts a new visitor with a fresh first touch. This tends to inflate new-visitor numbers and push credit towards whatever channel brought them back last, often direct or branded search.

Paid clicks suffer in a similar way. When a link carries decoration, meaning extra parameters such as click IDs appended to the URL, WebKit says it detects this and caps the expiry of cookies created in JavaScript on the landing page to 24 hours. So if a shopper clicks an ad, browses, leaves and buys two days later, the cookie that held the original campaign may be gone. The sale then lands on a different source. We'd read that as reasoning about how the rules interact, not as a measured loss for any one store.
Referrers add a third gap. WebKit downgrades all third-party referrers to their origin by default, for both the HTTP referrer header and document.referrer. You may see that a visit came from a site, but not which article, listing or page sent it. That hides the path that led to your landing page, which makes content and partner traffic look vaguer than it was.
Before you change any tracking, compare Safari and non-Safari trends in your analytics. Look at new versus returning visitors, the share of direct traffic and time between first visit and purchase. If Safari looks different in the ways above, that points to the cause. Treat the gap as directional rather than a number to correct for.
What Shopify customer events can do
Shopify's customer events, delivered through the Web Pixels API, give you one consistent stream of storefront behaviour. Shopify publishes and maintains a list of standard events, such as page_viewed, product_viewed and the checkout progression events. You subscribe once and get the same event names whichever theme the store runs. That tends to help because tracking no longer depends on every template carrying the right snippet. If the standard list doesn't cover something, the Web Pixels documentation says you can create, publish and subscribe to your own custom events.

Where the code runs matters too. Per the Web Pixels API docs, app web pixels load in a strict sandbox, while custom pixels load in a lax sandbox and are configured in the pixel manager in the Shopify admin. Custom pixels don't get the settings property that app pixels have. Either way, tags run in a controlled environment rather than loose in the theme. You can subscribe to several events at once with all_standard_events, or use all_events, all_custom_events and all_dom_events. The docs warn that what those subscriptions contain can change as events are added or modified, so check your handlers after Shopify updates.
Our tip: move tracking snippets pasted into the theme into a custom pixel or an app pixel, so you subscribe to events in one place. A merchant gets one spot to audit, and a developer stops hunting through Liquid files. One caution: a pixel running in the browser is still a browser script. WebKit's tracking prevention documentation says ITP deletes all cookies created in JavaScript, and other script-writeable storage, after 7 days. Where it detects link decoration, it caps the expiry of JavaScript cookies on the landing page to 24 hours. A tidier pixel setup doesn't lift those limits. It gives you a cleaner base to build on, and attribution recovery has to come from elsewhere.
What server-side tracking can and can't recover
Server-side tracking means your server, not the visitor's browser, sends events to analytics and ad platforms. That helps with one specific Safari problem. WebKit says ITP deletes cookies created in JavaScript, and all other script-writeable storage, after 7 days, and caps some at 24 hours after link decoration. A server doesn't depend on those script-written cookies to send an event. And an identifier set by your own server in a response header is a different thing from a JavaScript cookie, so those script-specific limits don't describe it in the same way. This tends to help because you stop leaning on the storage Safari trims most aggressively.
It has firm limits, though. A server can't recreate a visitor who never consented, and it doesn't make collection exempt from consent. WebKit also downgrades third-party referrers to their origins by default, in both HTTP referrer headers and document.referrer. Once that detail is gone, no server can put it back. Treat server-side as a way to send what you legitimately hold more reliably, not as a way to see more. Note too that WebKit blocks all third-party cookies by default, so an identifier meant to work across domains won't be rescued by moving the call to a server. MDN adds that SameSite=None and the Secure attribute are needed for cookies in a cross-site context, so set SameSite explicitly rather than trusting browser defaults.
Where this sits depends on your storefront. On a Liquid theme you don't control the storefront's server response, so server-side work usually means sending events from a backend you run or an app provides, fed by Shopify's customer events. On Hydrogen you own the server that renders the response, so you can set a first-party identifier there and forward events from the same place. Our tip: write down the identifier's lifetime and purpose, keep both as short and specific as you can justify, and only send what the visitor's consent allows.
Measuring within consent rules
Start by loading the Customer Privacy API properly. It is a browser-based JavaScript API, and you load it with loadFeatures using the consent-tracking-api feature. Once that succeeds without an error, it sits at window.Shopify.customerPrivacy. If you build your own cookie banner, this is the object it talks to. If you use an app for the banner, it should be doing this on your behalf.
Then check permissions with the right method. Don't rely on currentVisitorConsent alone. It returns 'yes', 'no' or an empty string, and Shopify's docs say it leaves out location and merchant configuration. The Allowed methods combine those factors with the visitor's choice. Use analyticsProcessingAllowed for analytics about how the storefront was used, and marketingAllowed for marketing, attribution and targeted advertising. This matters because regions differ. Where consent is required, non-essential purposes are off until the visitor agrees. Elsewhere, everything is allowed by default.
Listen for changes, but don't depend on the listener for the first answer. The visitorConsentCollected event fires only when consent changes, not when you add the listener. So read the current state on page load, then subscribe for updates. Record consent only on a real visitor interaction, such as accepting or declining in a banner, never automatically on their behalf. And don't read or modify Shopify cookies directly. The docs warn your processing will fail when new versions are released.
Treat data sale and sharing opt-out as a separate track. Shopify enforces it independently from consent, and the docs advise caution when combining the two. The GPC signal is honoured automatically in regions configured for data sale opt-out. For server-side opt-outs, use the dataSaleOptOut GraphQL API. If you send events from your own server, pass that opt-out through rather than assuming a consent check covers it.
Here is the tip. Gate each tag on the matching method: analytics tags on analyticsProcessingAllowed, ad and attribution tags on marketingAllowed. This tends to help because one blanket consent flag hides the difference between the two purposes. If you use Shopify's customer events, the Web Pixels API and its Pixel Privacy page are worth reading too. Then test in two places: a region set to require consent, and one that allows everything by default. The same visitor journey should behave differently, and seeing both is the quickest proof your gating works.
Pitfalls and what to do next
Four mistakes come up again and again. First, treating scrolling as engagement that keeps cookies alive. WebKit says user interaction is a click, tap or keyboard entry, and that scrolling is not considered user interaction. So a visitor who reads a long page but never taps won't help a classified domain. Second, expecting SameSite alone to rescue cross-site cookies. MDN explains that SameSite=None, with Secure, only makes a cookie eligible for cross-site use. Safari still blocks all third-party cookies by default, with access only through the Storage Access API, so set SameSite explicitly for consistency but don't expect it to change Safari's behaviour.
Third, treating server-side tracking as a way round consent. It isn't. Shopify's Customer Privacy API combines merchant settings, location and consent to decide what is allowed, and in regions that require consent, non-essential purposes stay off until the visitor opts in. Check analyticsProcessingAllowed or marketingAllowed rather than reading currentVisitorConsent alone, because that value leaves out location and merchant configuration. Record consent only when the visitor acts, and never read or edit Shopify cookies directly. Fourth, double counting. If a theme snippet and a pixel both fire the same purchase or add-to-cart event, your numbers inflate. This tends to happen during a migration, so remove the old tag the moment its replacement is live.
For next steps, start with an inventory of every cookie and tag on the store, noting who sets each one and whether it is written in JavaScript, since WebKit caps or deletes script-written storage. Then move tags to Shopify's customer events through the Web Pixels API, where standard events replace hand-built theme listeners. Next, decide whether server-side sending is worth it, and if so gate it on the same consent checks. Finally, compare Safari reporting before and after, using the same date ranges and a clear change date, so you're judging the change and not seasonal noise.
Get the weekly digest
One email every Monday: the week’s new guides and articles, plus the Shopify deadlines coming up and what’s been announced. Free, and you can unsubscribe in one click.
Sources
- Tracking Prevention in WebKit | WebKit · webkit.org · accessed 19 Oct 2026
- https://shopify.dev/docs/api/web-pixels-api · shopify.dev · accessed 19 Oct 2026
- https://shopify.dev/docs/api/customer-privacy · shopify.dev · accessed 19 Oct 2026
- Third-party cookies - Privacy on the web | MDN · developer.mozilla.org · accessed 19 Oct 2026
AI-assisted, reviewed by Scott. Every claim is checked against the sources above.