Identity · engineers · PMs
Safari will expire that cookie
ITP is not a bug in fbq; Safari classified the storage. Third-party cookies on vendor hosts are gone, and JavaScript-set first-party cookies that look like trackers get days of life, not months.
What actually expires
_fbp, _ga, and other JS-set first-party cookies on a site Safari considers to have tracker-like storage. A Set-Cookie max-age of two years does not override that cap. Returning shoppers in Safari look like new users in the pixel and still look like themselves in your account table.
Third-party cookies on facebook.com or google-analytics.com were already useless in Safari. If your only plan is that cookie, Safari traffic is unmatched by design. Chrome is not the QA browser for this. Measure Safari as its own cut in Events Manager and in GA4.
Document.cookie writes are the ones ITP treats as JS-set. HttpOnly cookies set by your own origin's Set-Cookie can live longer, which is why people moved ids to first-party server cookies. That move only helps if the checkout host receives them, which is a Domain attribute problem, not an ITP toggle.
Link decoration and bounce tracking
Click ids in the landing URL (fbclid, gclid) are how you recover a visit after the cookie died. They also decorate the URL, which is a signal browsers use to treat the subsequent storage as bounce tracking. Capture the id on first hit, persist it server-side, do not rely on the cookie lasting until purchase.
Stripping click ids in a 'clean URL' redirect to dodge decoration also strips the only join key that survives the cookie cap. Capture, then redirect if you must. Persist on the server before you 302.
CNAME cloaking is classified
CNAME'ing collect.yoursite.com to a vendor was the 2019 ITP workaround: first-party hostname, third-party collector, cookies that looked first-party. Safari classified cloaked CNAMEs. The hostname on your certificate does not make the collector yours. Storage under that CNAME is treated as tracker storage. Short life, not a loophole.
Server-side events from infrastructure you own are a different design. You POST hashed PII and stored click ids from your backend. You are not asking Safari to keep a JS cookie for two years. sGTM on a first-party host you actually operate is closer to that than a CNAME to a vendor. A CNAME to a vendor is the classified pattern.
What to build instead
Pixel plus CAPI, with click ids and hashed PII on the server event. First-party cookies still help in Chrome. They are a bonus in Safari, not the backbone. Rebuild fbc from stored fbclid when _fbc is missing. Omit fbp when _fbp is missing. Do not mint a fake _fbp to fill the hole ITP created.
Do not tell finance that Safari match rates will match Chrome because you set a first-party cookie. Measure Safari as its own cut. If Purchase CAPI has email, Safari can still optimize. If Purchase is pixel-only and the cookie is dead, Safari cannot.
ITP is not a Pixellint rule
Pixellint checks payload shape: fbp and fbc look like fb.N.timestamp.value, emails are hashed, IPs are not. It does not know the browser, and it does not simulate Safari. A valid fbp in the JSON can still be seven days stale relative to ITP's cap. That is an ops problem. Pixellint is not affiliated with Apple or Meta.
QA on iOS Safari with ITP on, with a click id, with a purchase days later. If CAPI still has email or rebuilt fbc, you designed around ITP. If the only identifier is a JS cookie, you did not.
// WRONG: rely on _fbp lasting until purchase
purchase.fbp = document.cookie.match(/_fbp=([^;]+)/)[1];
// RIGHT: persist click id at landing, send on CAPI even if cookie died
landing.fbclid = url.searchParams.get('fbclid');
db.save(sessionId, { fbclid: landing.fbclid, at: Date.now() });
// days later:
capi.user_data.fbc = 'fb.1.' + row.at + '.' + row.fbclid;
capi.user_data.em = sha256hex(order.email.trim().toLowerCase());
Check the artifact
Paste the pixel URL or JSON body into the
playground. Same engine as
pixellint validate. Nothing leaves the tab.