Click identifiers (gclid / gbraid / wbraid)
Click identifiers are how Google Ads matches a conversion to the ad click that drove it. There are three of them and they cover three different attribution paths.
| Identifier | Where it shows up | Why it exists |
|---|---|---|
gclid | Standard ad clicks: web search, display, most paid traffic | Has been the canonical Google Ads click identifier for years. |
gbraid | iOS in-app clicks landing on the web | Apple’s ATT framework restricts identifier passing; gbraid is Google’s web-side compatible token for those flows. |
wbraid | iOS in-app clicks where ATT consent was denied | Carries less data than gbraid but enables aggregated attribution. |
If your ads run on web search, you’ll see gclid everywhere. If they also run iOS app installs that drive web traffic, the other two start showing up. Trackbridge captures all three the same way and forwards all three.
Capture: from URL to in-memory state
Section titled “Capture: from URL to in-memory state”When createBrowserTracker runs (typically once per page load), it reads window.location.search and looks for these query parameters. Whichever it finds, it puts in the tracker’s internal state.
https://yoursite.com/landing?utm_source=google&gclid=Cj0KCQ...After init, tracker.getClickIdentifiers() returns:
{ gclid: 'Cj0KCQ...' }If the URL has multiple of gclid, gbraid, wbraid, the SDK keeps each one. Real-world traffic only has one at a time, but the SDK doesn’t enforce that.
Persistence: first-party cookies
Section titled “Persistence: first-party cookies”After capture, if clickIdentifierStorage === 'cookie' (the default) and consent permits, the SDK writes one cookie per identifier:
_tb_gclid=Cj0KCQ...; Secure; SameSite=Lax; Path=/; Expires=<+90d>Domain= is added if you set cookieDomain on the tracker config — useful for cross-subdomain attribution (www.example.com capturing on landing, app.example.com reading on conversion). See Cross-subdomain tracking.
The cookies are first-party (your domain), Secure (HTTPS only), and SameSite=Lax (sent on top-level navigations but not third-party iframes). They are not HttpOnly because the SDK has to read them from JavaScript.
The full table of cookie attributes is on the Cookies reference page.
Persistence: gated on consent
Section titled “Persistence: gated on consent”When consentMode: 'v2' is configured, the SDK only writes the cookies if ad_storage is 'granted'. Until that happens, captured values live only in memory.
The flow:
- User lands on
?gclid=Cj0KCQ.... SDK capturesgclidinto memory. - CMP banner is shown. User has not interacted yet —
ad_storageis unknown, treated as not-granted. - SDK does not write
_tb_gclidcookie. - User clicks “Accept” on the banner. CMP fires
tracker.updateConsent({ ad_storage: 'granted', ... }). - SDK writes
_tb_gclidcookie at this point — the in-memory value is persisted. - On a later page load, the SDK reads the cookie and re-hydrates the in-memory state, even without a
gclidin the URL.
If the user clicks “Decline”, the in-memory value is held for the lifetime of the tab and discarded on close. Subsequent page loads have no record of the click.
This is the right GDPR/Consent Mode v2 behavior. See Consent Mode v2.
Forwarding to the server
Section titled “Forwarding to the server”The browser tracker captures click IDs. The server tracker needs them too — it has no view of the URL the user landed with. Forward them with whatever request creates the order:
import { tracker } from '@/lib/tracker.client';
const clickIds = tracker.getClickIdentifiers();
await fetch('/api/checkout', { method: 'POST', body: JSON.stringify({ ...orderInput, clickIds }),});Store the IDs alongside the order so they’re available later, when the conversion fires (often from a webhook, hours after the user closes their browser):
const order = await db.orders.create({ ...orderInput, gclid: clickIds.gclid ?? null, gbraid: clickIds.gbraid ?? null, wbraid: clickIds.wbraid ?? null,});When the server-side conversion fires:
await serverTracker.trackConversion({ label: 'purchase', transactionId: order.id, gclid: order.gclid ?? undefined, gbraid: order.gbraid ?? undefined, wbraid: order.wbraid ?? undefined, // ...});If you don’t forward, the server-side conversion still fires but Google can’t attribute it to a click. It still helps with the conversion count (the dedup partner of the browser fire), but not with attribution beyond what the browser-side fire already carried.
What this does NOT do
Section titled “What this does NOT do”- It does not call the Google Ads API to validate the click. The
gclidyou have is the one Google gave the user; the SDK trusts that. - It does not decay or rotate values. A 90-day-old
gclidis sent as-is. Google’s matcher decides if it’s still attributable. - It does not capture other tracking parameters (
fbclid,msclkid, etc.). Trackbridge is a Google Ads / GA4 SDK; the names are hardcoded to the three Google identifiers.
See also
Section titled “See also”tracker.getClickIdentifiers()— the API.- Cookies — the
_tb_*table. - Cross-subdomain tracking — sharing identifiers across hosts.