Your iOS conversion data is bleeding out. Apple’s App Tracking Transparency (ATT) and Intelligent Tracking Prevention (ITP) have pushed the global ATT opt in rate to a stagnant 27%. Meanwhile, iOS CPI inflation is up 19% year over year, averaging $5.20.

You are paying more for less visibility. The fix is not another workaround in the browser. The only reliable solution is a server-side GTM iOS attribution fix combined with Google’s on-device measurement (ODM).

This is the framework that recovers the 10-15% of conversions you are currently losing on Safari and iOS. After migration, you can expect an average 12% lift in measured conversions and an 8% increase in ROAS.

No hyped pixels, no shady redirects. Just a clean, privacy preserving pipeline that keeps click IDs alive inside first party cookies.

In this guide, you will build exactly that. You will enable ODM in your iOS app, deploy a server-side GTM container, configure click ID preservation, and validate that conversions hit Google Ads.

What You’ll Build

A complete iOS attribution system where events are processed on the device, relayed to your server-side GTM container, enriched with preserved click IDs (gclid, wbraid), and sent to Google Ads. No personally identifiable information (PII) leaves the device.

Prerequisites

  • A Firebase project linked to Google Analytics 4 (GA4), with the Firebase SDK integrated into your iOS app targeting iOS 12 or later.
  • A Google Ads account with iOS App campaigns set up and conversion tracking configured.
  • Two Google Tag Manager containers: a web container (if you also track web) and a server container (sGTM).
  • DNS access to create a CNAME record for your server endpoint (e.g., gtm.yourdomain.com).
  • A deployment choice: self-hosted Docker ($20-$30/month), Google Cloud Run ($12-$15/month for 500k events), or a managed provider like Stape ($99-$149/month).

Step 1: Enable On-Device Measurement in Your iOS App

Google’s ODM processes conversion events directly on the iPhone. No PII ever leaves the device.

There are two variants: Event Data and First-Party Data. Google recommends implementing both for maximum performance.

Add the Library

Add the GoogleAdsOnDeviceConversion library to your Xcode project. Using CocoaPods, add this to your Podfile:

pod 'FirebaseAnalytics/Core'
pod 'GoogleAdsOnDeviceConversion'

Or via Swift Package Manager, search for GoogleAdsOnDeviceConversion and add it to your target.

Enable in Info.plist

Set the flag in your app’s Info.plist:

<key>GOOGLE_ADS_ON_DEVICE_CONVERSION_EVENT_DATA_ENABLED</key>
<true/>

The default is YES, but confirm it is present. For the first-party data variant, you need explicit user consent (email or phone) passed via the ODM API. Google’s Firebase documentation shows how to implement consent collection.

Configure Firebase to Send Events to Your Future sGTM Endpoint

In your Firebase project, add the server endpoint as a custom measurement URL. You will fill in the actual domain after deploying your server container in the next step. For now, prepare the Firebase config file to point events to your subdomain.

After this step, your app will generate Firebase events that include ODM signals. Those events need a home. That is where the server container comes in.

Step 2: Deploy a Server-Side GTM Container

This is the engine that receives events from your app, enriches them, and forwards them to Google Ads without relying on browser cookies that iOS kills.

Create the Container

In Google Tag Manager, click Create Container, select Server as the container type, and name it “iOS Attribution Fix”. Note your container ID (starts with GTM-).

Deploy

For Google Cloud Run (recommended):

gcloud run deploy sgtm-container \
  --image=gcr.io/cloud-tagging-1248/gtm-cloud-image:stable \
  --platform=managed \
  --region=us-central1 \
  --allow-unauthenticated \
  --memory=512Mi

If using Stape, paste your container ID into their dashboard and they handle the rest. Either way, you end up with a HTTPS endpoint.

Point Your Domain

Add a CNAME record in your DNS:

gtm.yourdomain.com  CNAME  [your-cloud-run-url]

Enable HTTPS via a managed certificate (Cloud Run does this automatically if you map a custom domain). Then update your Firebase config to send events to https://gtm.yourdomain.com as the custom measurement endpoint.

Add Clients and Secret

In the server container, the GA4 client is added by default. This client listens for Firebase events.

Add a Secret (under Admin > Server container > Secrets) to prevent spam. Use it as a custom dimension in your GA4 configuration tag.

This secret ensures only your app’s traffic reaches the server. Simo Ahava’s server-side tagging guide emphasizes this as a critical anti-spam measure.

Step 3: Configure Click ID Preservation

This is where the money lives. Without this step, iOS strips gclid and wbraid from URLs before your app or website can capture them. We bypass that by using a custom parameter alias and server side rewriting.

Add a Query Replacer Variable

In your server container, go to Templates and search for the Query Replacer variable template by stape io. Add it to your workspace.

Create a variable using that template and map your custom parameters:

| Incoming Parameter | Replaced With    |
|--------------------|------------------|
| c_gci              | gclid            |
| c_wbraid           | wbraid           |
| c_src              | utm_source       |
| c_med              | utm_medium       |

This means your ad URLs should use c_gci instead of gclid. iOS link tracking protection does not recognize c_gci as a tracking parameter, so it survives.

The server then restores the standard name. Analytics Mania’s tutorial covers this exact technique with UTM preservation.

Add a Conversion Linker Tag

This built in tag writes the click ID into a first party cookie named FPGCLAW via the server’s Set-Cookie header. That cookie is not subject to ITP blocking because it comes from your own domain.

Create a Conversion Linker tag in the server container and set it to fire on the All Pages trigger (which listens for page_view events from the GA4 client).

Add a Google Ads Conversion Tag

Create a Google Ads Conversion Tracking tag in the server container. Configure it with your conversion ID and label.

Set the source to the GA4 client and the event name to your conversion event (e.g., purchase). The tag will automatically read the restored gclid from the event data and forward it to Google Ads.

If you need remarketing, add a GA4 Configuration tag that sends the enriched events back to GA4.

Step 4: Validate and Publish

The most common mistake is publishing before testing. Do not skip this.

Use Preview Mode

Enable preview mode in both your web container (if any) and your server container. On the server, open the preview URL and inspect the request payloads. You should see gclid or c_gci present in the incoming event, and the query replacer variable should be transforming it.

Fire a test conversion from your iOS app. Use the Firebase debug view or a real device. Check that the server container logs show the event received, the click ID preserved, and the conversion sent to Google Ads.

Validate in Google Ads

Go to Conversions in your Google Ads account. Look for the test conversion in the diagnostic logs. It should appear with a valid click ID, not as “unattributed”.

If it shows “direct” or no click source, your query replacer or conversion linker is misconfigured.

Common Pitfalls

  • Forgetting to publish: Preview mode is not live. You must publish the server container after testing.
  • Misordered dataLayer: If you use a web container to forward data, ensure the dataLayer push happens before the GTM snippet.
  • Server resource exhaustion: Insufficient memory or CPU on your cloud run instance causes 403 errors and dropped events. Scale up or use a managed provider that auto scales.
  • Not isolating containers per app: Using one server container for multiple apps can mix click IDs. Create separate containers if you run multiple Firebase projects.

After validation, publish the server container and monitor your iOS conversion data for at least 48 hours. You should see an increase in attributed conversions, especially for Safari and in app traffic.

Real world impact: A DTC brand running $50k/month in iOS App campaigns saw measured conversions jump 14% and CPA drop $0.80 within one week of this setup. Their cost for the server container on Cloud Run was $14/month. That is a $7,000 monthly savings from a $14 server.

This system does not just fix iOS attribution. It also future proofs your tracking against upcoming Safari and browser restrictions. The same server container can handle web events, consent mode, and even server side integrations with other ad platforms.

If you would rather have a team build this entire pipeline for you, we offer a Growth Sprint: a fixed scope two week build that includes the server container deployment, ODM configuration, click ID preservation, and full validation. No retainer, just a $1,500 flat fee. Learn more about the Growth Sprint.

Next Steps

You now have a working server-side attribution fix for iOS. But attribution is only one piece of the performance puzzle.

To truly scale, you need to combine clean data with systematic creative testing and fast lead response. Misattribution often hides weak creative or slow follow up. Now that you can trust your numbers, start optimizing what actually moves revenue.

If you want an immediate audit of where your current tracking is leaking conversions, run our free AI audit. It scans your site and ad accounts in minutes and shows you exactly which iOS signals you are missing.

Cover photo by Christopher Burns on Unsplash.