Follow me on LinkedIn - AI, GA4, BigQuery

In this article, I show you step by step how I use Claude for conducting GA4/GTM/Server Side Tagging audits in great detail even when I do not have access to the analytics accounts.

I would use a live website for demonstration purposes. So it's not all theoretical for you.


I also share the prompts I use so that you can use them straightaway to uncover insights which people even with accounts access can't easily discover on their own.

Finally, I promise you that after going through this article, you will never think of doing audits without Claude again, and your audit reports will be 100x better.

Copying and pasting all network requests is better than supplying source code to Claude for GA4/GTM/SS Tagging Audit.

Source code only shows what's declared, not what executes. 

Four specific reasons this matters for tagging audits:

#1 Modern frameworks don't render tags into HTML at all. 

Most sites today are SPAs or server-rendered apps (React/Next.js, Vue, etc.) where the actual tracking logic lives inside minified, hashed JS bundles referenced only by opaque filenames. 

The HTML just says "load this bundle"; it doesn't show what the bundle does. Reading source in this case tells you nothing about what fires.


Any competently built CMP delays tag firing until the user consents. 

A pre-consent HTML capture will show zero tracking scripts even if the site has ten of them, because they're all dynamically injected by JS after a consent event, not present as static <script> tags an audit can grep for.


#3 Server-side tagging is invisible in source code by definition. 

The entire point of sGTM is that the tag-firing logic moves off the client and onto a server the site owns. 

From the browser's perspective, all you'd see in source is a single script tag pointing to a first-party-looking subdomain (e.g. something like tags.clientdomain.com/gtm.js). 


Whether that subdomain is a Stape container, a self-hosted GTM server, or something else entirely; what it forwards to; and which measurement IDs/pixel IDs it's relaying, none of that is visible in markup. 

It only shows up as subsequent outbound network calls from that subdomain to Google/Meta/etc, which only appear in a network capture, never in source.


#4 Source code doesn't show sequencing, deduplication, or actual destinations. 

A GTM container tag in source just says "GTM is present." 

It doesn't tell you which triggers actually fired, which tags fired as a result, what data was in the payload, whether GA4 hits went directly to Google or were routed through a first-party proxy first, or whether Consent Mode signals (gcs, gcd parameters) were passed correctly. 


All of that lives in the request URL, query parameters, timing, and response headers, which only a network capture exposes.

In short, 

Source code shows you the wiring diagram as drawn; network requests show you what actually happened when someone flipped the switch. 

For an audit, you care about the latter (what data is genuinely being collected and where it's genuinely going), not what's theoretically configured.

How to capture network requests and supply them to Claude for tag auditing.

First, make sure that you are not using any ad blocker, privacy extension, or VPN that blocks or interferes with analytics tracking and data collection.

Second, confirm third-party cookies aren't globally blocked in your settings (chrome://settings/cookies).

You want "Allow third-party cookies" (or whatever matches the real-world audience you're auditing), unless you're deliberately testing the blocked-cookie experience as a separate scenario.


Third, use a regular (non-Incognito) browser profile that's been freshly cleared of cookies and site data, rather than an Incognito profile.

Incognito blocks third-party cookies by default in Chrome, which suppresses or alters cookie-dependent tag behaviour (identity stitching, remarketing list building, cookie-sync pixels) in ways a real visitor wouldn't experience, so it can make an audit look cleaner or sparser than reality.

A cleared normal profile gives you the same clean starting state without that distortion.

  1. In the cleared profile, open DevTools (F12 or Cmd+Opt+I) before navigating to the site. This ensures you capture the very first request, not just what loads after DevTools opens.
  2. Go to the Network tab. Enable "Preserve log" (critical; without this, navigating between pages or triggering redirects wipes the log).
  3. Navigate to the site's homepage. Do not click accept/reject on the cookie banner yet.
  4. Let the page fully settle (wait for network activity to go idle, 5-10 seconds after onLoad).
  5. Browse the website (while still leaving the cookie banner unactioned.) Complete conversions (like filling out a form or making a test purchase) if required. This surfaces any pre-consent tags tied to navigation or interaction triggers, not just the initial pageview.
  6. Under ‘Network’ tab in developer console, right-click anywhere in the empty request list → "Copy all as HAR with content (Sanitized)". Save this as a text file for your pre-consent baseline.

This pass tells you what fires before any consent decision. This is what regulators (ICO, CNIL, etc.) actually care about, and it often reveals tags firing that shouldn't be (a common compliance failure: tags configured to "fire on all pages" in GTM without a proper consent-gate blocking trigger).


#7. Use the following prompt in Claude and attach your network request file:

Prompt:

Attached is the network request file for the website: https://octopus.energy/ before I accepted cookies (pre-consent state). Identify any GTM or analytics implementations (list out all the trackers), including server-side proxied versions. Explain what you found and how you identified it. Also tell me which plugins are being used for analytics tracking (like Google Analytics) and advertising, if any.

Following is the Claud response, which is worth reading:

Here's what I found in the pre-consent HAR for octopus.energy (214 requests across the homepage and /order/solar/ pages, captured 09 Jul 2026).


How I identified things

I pulled all unique request domains, then decoded the obfuscated query parameters on the self-hosted analytics paths (base64) and cross-checked response headers (server, content-type) to work out what's actually running behind first-party-looking URLs.

No response bodies were captured in this HAR, so vendor identification is based on domain, path structure, decoded params, and header fingerprints — not on reading the JS source.


1. Server-side GTM container (self-hosted proxy).

  • sst.octopus.energy/gtm.js?id=GTM-PTCS2P&gtg_health=1 — confirms container GTM-PTCS2P, health-check ping
  • sst.octopus.energy/aureinfkaw.js?6ba1j74=aWQ9R1RNLVBUQ1MyUA%3D%3D&page=2 — the obfuscated path decodes to id=GTM-PTCS2P, i.e. this is the sGTM-served client-side gtm.js loader, renamed to dodge ad blockers
  • Response headers show server: nginx on sst.octopus.energy — consistent with the sGTM setup you flagged before (proxied via Stape)

2. Google Campaign Manager 360 / Floodlight — proxied server-side

  • sst.octopus.energy/aureinfkaw/d?4f83f62c=L2d0YWcvanM... decodes to /gtag/js?id=DC-11164394&cx=c&gtm=4e6771
  • The DC- prefix identifies this as a Campaign Manager 360 (Floodlight) tag being loaded through gtag.js, proxied through the same sGTM domain rather than called directly to Google. This is new/notable — it's an ad measurement tag firing pre-consent, disguised as first-party traffic.

3. Google Analytics — adblocker probe only

  • analytics.google.com/g/collect?a=adblocker_check (HEAD request)
  • This is not a real GA4 pageview hit — there's no tid=G-... measurement ID, no event params. It's Google's standard adblocker-detection ping, sent directly to Google (not proxied). No actual GA4 collect hits fire pre-consent in this capture.

4. Unidentified first-party-proxied behavioural tracker

  • Domain: tk.octopus.energy, loading 50309/tag_50309_1.js, then firing to anonymize/t/s.gif, anonymize/p/v.gif, anonymize/t/err.gif
  • Query params use an esv* naming convention (esvid, esvtid, esvuid, esvccid, esvrsl, esvsite, wiz_cnst) and the JS exposes a global object called wiztrack
  • This is a full pageview + session tracker: it generates a persistent visitor ID (esvuid=372b3e4919f466ceeb8) that's identical across both page loads in this capture, and it logs page titles, screen resolution, and referrer
  • I could not conclusively match this parameter set/object name to a named vendor via search — same as the unidentified tracker flagged in the earlier octopus.energy audit. Worth a direct check of the page source or asking the client's dev team, since it's actively profiling visitors before consent is given, which is a compliance point worth flagging regardless of vendor ID.

5. Datadog RUM + Session Replay + Logs

  • browser-intake-datadoghq.eu/api/v2/rum, /api/v2/replay, /api/v2/logs
  • ddtags show service:oegb-goods, SDK versions 5.16.0 (RUM/Replay) and 5.6.0 (Logs)
  • This is real user monitoring plus session replay, firing pre-consent. Replay is the more sensitive one from a consent standpoint since it can capture on-screen interaction.

6. Sentry (error monitoring)

  • o4509005530333264.ingest.de.sentry.io — sentry.javascript.nextjs/10.41.0, sampled at 10% (sentry-sample_rate=0.1)
  • Pure error/performance monitoring, not marketing analytics.

No Meta pixel, TikTok pixel, Microsoft/Bing UET, or HubSpot calls fired in this snapshot — consistent with those being properly gated behind the CMP and only firing after consent (unlike the DC-11164394 Floodlight tag and the tk.octopus.energy tracker, which fire regardless).


Non-tracking, for completeness

Stripe (js.stripe.com, m.stripe.network, m.stripe.com) is payment/fraud (Radar), Trustpilot widget is proxied through oe-trust-pilot.vercel.app, auth.octopus.energy is their own OAuth login flow, and the gstatic.com/play.google.com/log calls are Google's own telemetry for an embedded "One Google" sign-in widget — none of these are marketing/analytics trackers.


On "plugins"

There's no plugin architecture here — this isn't WordPress/Shopify. It's a Next.js app hosted on Vercel, so GA4, Floodlight, and the unidentified tracker are all wired in directly through the server-side GTM container (GTM-PTCS2P) or hardcoded script tags, not via installable plugins.

If you want, I can pull the actual tag_50309_1.js and check its headers/redirect chain to try to pin the vendor down, or compare this pre-consent snapshot against the post-consent HAR to map exactly what the CMP gate adds.

Phase 2: After accepting cookies (full consented state)

  1. In the same session (keep "Preserve log" on, don't clear it).
  2. Click "Accept all" on the cookie banner.
  3. Watch the network panel for the burst of newly unlocked requests (GA4 hits, ad pixels, sGTM calls).
  4. Browse the same pages/interactions again - this is useful beyond just triggering event tags: it lets you directly compare identical user actions across both passes and see exactly which requests are new/different post-consent, rather than comparing two structurally different browsing sessions.
  5. Under ‘Network’ tab in developer console, right-click anywhere in the empty request list → "Copy all as HAR with content (Sanitized)". Save this as a text file for your post-consent requests.

Note: Browsing the same pages/interactions in both phases (rather than different ones) is what makes the before/after diff meaningful. If in phase-1 you visit page A and in phase-2 you visit page B, you can't cleanly attribute new requests to "consent unlocked this" versus "this page just has different tags." Keep the click-path identical across phases.


#6 Use the following prompt in Claude and attach your post consent network request file:

Prompt:

Attached is the network request file for the website: https://octopus.energy/ after I accepted cookies (post-consent state). Identify any GTM or analytics implementations (list out all the trackers), including server-side proxied versions. Explain what you found and how you identified it. Also tell me which plugins are being used for analytics tracking (like Google Analytics) and advertising, if any.

Following is the Claud response which is worth reading:

This file has 821 requests (vs 214 pre-consent) and pulls in a genuinely large stack. Same identification method as before: domain/path inventory, decoding the base64 params on the sst.octopus.energy proxy paths, and diffing against the pre-consent snapshot to see exactly what the consent banner was gating.


Server-side GTM (confirmed vendor: Google's official server container, fronted via Stape)

sst.octopus.energy now proxies far more than the health-check ping seen pre-consent. Decoding the obfuscated paths:

  • /gtag/js?id=GTM-PTCS2P — the web container loader (same as pre-consent)
  • /gtag/js?id=G-Y9JNFMEXNR&cx=c&gtm=4e6771 — GA4 gtag library, now proxied server-side
  • /gtag/js?id=DC-11164394&cx=c&gtm=4e6771 — Campaign Manager 360/Floodlight loader (same as pre-consent)
  • /g/collect?v=2&tid=G-Y9JNFMEXNR&... — actual GA4 Measurement Protocol hits, sent server-side
  • /data?v=2&event=page_view&richsstsse — Google's server-side "rich" event format
  • /_/service_worker/{id}/sw.js + sw_iframe.html — Google's First-Party Mode service worker, which extends first-party cookie durability by proxying Google's cookies through your own domain

Separately, stapecdn.com/dtag/v8.js loads directly — this is Stape's own client-side data-relay library, which confirms Stape (not a generic nginx box) is the infrastructure behind sst.octopus.energy.

GA4 property confirmed: G-Y9JNFMEXNR — single property, matches what you found in the earlier audit. It's also linked to Google Ads audiences via /ads/ga-audiences?...tid=G-Y9JNFMEXNR.


  • Three Google Ads conversion IDs firing view-through conversion, remarketing (rmkt/collect), and 1p-user-list (audience) pings: 931004078, 734705890, 16937214810
  • Campaign Manager 360 Floodlight activity tag confirmed again: src=11164394 (i.e. DC-11164394), firing to both ad.doubleclick.net/activity and adservice.google.com/ddm/fls/z
  • All of this passes Google Consent Mode v2 signals (gcs=, gcd=, npa=, dma= params), so the site is consent-mode aware rather than just switching tags fully on/off

Meta (Facebook) Pixel — new post-consent

  • connect.facebook.net/en_US/fbevents.js + facebook.com/tr/?id=1692770077658096&ev=PageView
  • Pixel ID: 1692770077658096
  • Uses Meta's "SmartSetup" automatic event matching, and I can see a hashed-email parameter (hme=) being sent — that's Advanced Matching, meaning if a user is logged in, a hashed version of their email is passed to Meta alongside the pixel event

TikTok Pixel — new post-consent

  • analytics.tiktok.com/i18n/pixel/events.js?sdkid=D3R51HBC77U53GBVROC0
  • Plus an IPv6 enrichment call to analytics-ipv6.tiktokw.us (device/network fingerprinting supplement)

Microsoft/Bing — new post-consent

  • UET tag ID: 11008170 via bat.bing.com/bat.js
  • c.bing.com/c.gif?Red3=BACID_36181644 — Bing Ads click-ID capture (msclkid persistence)
  • Microsoft Clarity (session recording/heatmaps), project ID nydnyt0khk, loaded via www.clarity.ms, scripts.clarity.ms, b.clarity.ms/collect — and it's explicitly linked to the same Bing UET ID (clarity.ms/tag/uet/11008170), so Clarity and Bing Ads share identity

HubSpot — full suite, portal ID 26119526

  • hs-scripts.com (base tracking), hs-analytics.net (analytics), hsadspixel.net (ad-platform sync pixel), hs-banner.com + hubspot.com/web-interactives-embed.js (on-site banners/CTAs), cta-eu1.hubspot.com, hsforms.com (forms), hubapi.com (config API), and track-eu1.hubspot.com/__ptq.gif / __ptc.gif (pageview + granular click tracking, capturing element text/class of clicked buttons)
  • EU data-residency endpoints (-eu1-) throughout

Other trackers, new post-consent

  • Ahrefs Web Analytics (analytics.ahrefs.com) — lightweight first-party-friendly analytics
  • Tapfiliate (script.tapfiliate.com) — affiliate/referral tracking
  • WURFL.io — device/browser capability detection (fingerprinting-adjacent, feeds personalization rather than ads directly)

The unidentified tk.octopus.energy tracker — now with a real clue

This is the one I couldn't ID from headers alone last time.

Comparing the two captures side by side gave me something useful: the collection paths change shape once consent is granted.

  • Pre-consent: /anonymize/t/s.gif, /anonymize/p/v.gif
  • Post-consent: /t/s.gif, /p/v.gif (same gif endpoints, anonymize/ prefix dropped), plus a new /anonymize/t/cnst.gif call and a new wiz_rfir=1 param appearing on the post-consent hits

That's a genuine consent-gated dual mode: before consent it hits an "anonymize" path (presumably truncating/hashing identifiers or skipping persistent ID storage), and after consent it switches to the full, non-anonymized collection path with the same persistent visitor ID (esvuid=372b3e4919f466ceeb8) carried over.

So whatever this vendor is, it's properly respecting the consent signal at the endpoint level — which is good practice — but I still can't name the vendor from URL/header fingerprints and public search alone.


The cnst.gif hit strongly suggests it's listening for the consent event directly (via GTM's dataLayer or a postMessage) rather than just reading a cookie.

Unchanged from pre-consent: Datadog RUM/Replay/Logs, Sentry error monitoring, Stripe, Trustpilot widget, the OneGoogle sign-in widget, and auth.octopus.energy — none of these are consent-gated.


No third-party CMP found

I checked for OneTrust/Cookiebot/TrustArc/Didomi/Usercentrics domains and found none.

The consent banner is either fully custom-built or self-hosted, gating tags via Google Consent Mode v2 signals baked directly into the GTM container rather than a bought-in CMP platform.


On "plugins"

Same caveat as before — this is a Next.js/Vercel app, not a plugin-based CMS, so everything above is wired in through GTM-PTCS2P (client-side + server-side) or hardcoded script tags, not installable analytics/ads plugins.

If it'd help, I can build a clean before/after table (tag, ID, vendor, fires pre-consent Y/N) formatted for a client-facing audit doc — useful for a Consent Mode compliance section given the DC-11164394 Floodlight tag and the tk.octopus.energy tracker both fire before consent is given.


Prompt:

build a clean before/after table (tag, ID, vendor, fires pre-consent Y/N) formatted for a client-facing audit doc

Download the latest version of your GTM containers (both web and server) and upload them to Claude.

Anyone with at least ‘Read’ access to the GTM containers can export them via Admin → Export Container. 

If you get that file, Claude can parse it directly (tag names, trigger conditions, consent settings per tag, variable definitions) which would be much more precise than reverse-engineering from network traffic.

You can then ask Claude any question to understand how a particular tag/trigger/variable behaves.

Prompt to use when uploading GTM container exports (web + server):

Attached are two GTM container exports for [client/site name]:

  • Web container: [GTM-XXXXXXX], version [n]
  • Server container: [GTM-XXXXXXX], version [n], hosted on [Stape / GCP App Engine / Cloud Run / unknown — say if you don't know]

Run a full tag audit covering both containers and how they connect to each other:

1. Per-container inventory Tag name, type (GA4 event/config, Google Ads conversion, Floodlight, Conversions API/CAPI, custom HTML/image, etc.), firing trigger(s), and active/paused status — separately for web and server.

2. Web-to-server connection

  • Does the web container's GA4 config tag (or any tag) point its transport URL at the server container's domain? Pull the exact server_container_url / transport URL value from the web container config.
  • Does the server container's client (GA4 Client, or Custom Client) match that same domain, and is it actually receiving hits — or is the wiring done outside GTM entirely (site header, theme file, CMS setting)? Flag if you can't confirm the connection from the exports alone.
  • Map every measurement ID / conversion ID that appears in both containers and confirm it's the same ID doing the same job on each side, not two different properties colliding.

3. Server-side tag behaviour

  • For each server-side tag, check redactVisitorIp / redactDeviceInfo type settings — flag anywhere IP or device data is passed through unredacted.
  • Identify which client-side tags are duplicated server-side (e.g. Meta Pixel firing client-side AND forwarded via CAPI) — flag double-counting risk.
  • Note any custom transformations/clients configured on the server container and what they modify.

4. Consent settings — both containers

  • Which tags (web and server) have Additional Consent Checks configured, and for which consent types (ad_storage, analytics_storage, ad_user_data, ad_personalization)?
  • Flag every tag with no consent settings applied — these fire regardless of the CMP.
  • Server containers often don't see consent signals unless they're explicitly forwarded from the web container — check whether consent state is actually being passed through (e.g. via the GA4 client reading gcs/gcd params) or whether server-side tags fire blind to consent.

5. Cross-reference against this HAR finding: [paste the relevant row(s), e.g. "Campaign Manager 360, DC-11164394, fires pre-consent via sst.octopus.energy"] — trace which container and which specific tag/trigger is responsible, and whether the fix belongs in the web container (trigger/consent check) or the server container (client config, transformation).

6. Trigger and variable hygiene

  • Flag "All Pages"/"Page View" triggers with no exception conditions.
  • Flag custom JS variables or cookie variables reading PII without hashing.
  • Flag hardcoded IDs that should be variables.

7. Output as a severity-tagged findings table (Critical / High / Medium / Low) — one row per finding, tag/container name, the issue, and the fix — same format as [brainsensei / prior report]. Include a short "how the two containers talk to each other" summary paragraph up front, since that relationship is usually the part nobody's documented.

Give me specific tag and trigger names throughout — I need to hand this straight to dev.

The following is a sample response if I had access to the GTM containers for octopus.energy.

Web container: GTM-PTCS2P, v58 (published)

Server container: GTM-5T9XQ2R, v31 (published), hosted via Stape on sst.octopus.energy


How the two containers talk to each other.

The web container's GA4 Configuration tag (GA4 – Config – Main) has its transport URL set to https://sst.octopus.energy, so all client-side GA4 events route through the server container rather than hitting Google directly.

The server container has a matching GA4 Client listening on that same path, which forwards events to a GA4 Server tag pointed at G-Y9JNFMEXNR — same measurement ID on both sides, correctly matched.


Separately, a Custom Client on the server container intercepts requests under /aureinfkaw* and routes them to a Floodlight server tag (DC-11164394) — this is the path responsible for Campaign Manager loading pre-consent, and it sits entirely in the server container, invisible from the web container side.

Consent state is passed from web to server via the standard gcs/gcd query parameters appended by GTM's Consent Mode integration, and the server container does read them — but only two of the six server-side tags actually check them before firing.


Web container — tag inventory (excerpt).

Finding: Floodlight – Loader fires on All Pages with no consent check attached, and its trigger type is a plain Page View rather than Consent Initialization — meaning it fires before the Consent Mode default tag has a chance to set ad_storage=denied. This is the root cause of the pre-consent Floodlight load seen in the HAR. 


Server container — tag inventory (excerpt).

Finding: Floodlight – Server has no consent gating configured at all — it fires for every request the Custom Client accepts, regardless of what gcs/gcd say. This is the second half of the same root cause: even if the web-side trigger were fixed, the server tag would still fire blind.

Finding: redactVisitorIp is set to false on all four active server tags — full IP addresses are being forwarded to Google and Meta from the server container.


Duplication check

Meta Pixel fires client-side (Meta Pixel – Base) and is forwarded server-side (Meta CAPI – Forward) using the shared GA4 Client. Both use the same event_id deduplication parameter, so this is correctly deduplicated — not a double-counting issue, just worth confirming Meta's Events Manager shows matched events rather than two separate ones.


Severity-tagged findings.