The Chrome DevTools Network Panel for GA4 Hits

Published 

8 min read

ShareLinkedInX
Title card reading Chrome DevTools Part 4: Network: Read, edit, block and resend GA4 hits, beside a screenshot of the Chrome DevTools Network panel

Everything else in DevTools shows what the page intended: the dataLayer push, the trigger's selector, the tag library. The Network panel shows what actually left the browser. When a GA4 report and a developer disagree, the Network panel settles it.

It does more than show requests. You can change what the server sends back, edit a hit and send it again, block a tag the way an ad blocker would, and hand a whole session to someone else as a file. This guide covers all of it, using real hits from the Google Merchandise Store in Chrome 156.

Part of the Chrome DevTools for tracking series. Overview · Elements · Console · Sources · Network · Recorder

How to open it#

Press F12 (Cmd+Option+I on Mac) and click the Network tab, or press Ctrl+Shift+P and run Show Network. The panel only records while it is open, so reload the page with Ctrl+R once it is showing.

The Network panel is open on the Google Merchandise Store; Keep log is ticked, collect?v=2 is typed into the filter, the page is reloaded, a GA4 hit is clicked and its Payload tab is opened

Keep log, filter to collect?v=2, reload, then click a hit and open Payload: the core loop of GA4 QA.

Two checkboxes in the toolbar matter before anything else:

  • Keep log. Older versions call it Preserve log. It keeps requests across navigations, so the hit fired by a form submit survives the redirect to the thank-you page. Forgetting it is the most common reason people conclude "the event didn't fire" when it did.
  • Disable cache. It makes sure you are testing the latest published GTM container, not a cached copy of the previous version. It only applies while DevTools is open.

Find the hits#

The filter box accepts plain text and properties:

  • collect?v=2 matches GA4 web hits and nothing else. The tempting /g/collect also catches Google Ads and Floodlight requests (collect?rcb=…), which on a busy site outnumber the GA4 hits. On server-side GTM, filter on your own collection path instead.
  • domain:*.facebook.com isolates one vendor.
  • method:POST or status-code:204 narrows further. GA4 responds 204 No Content to a hit it accepted.
  • A leading minus inverts a filter: -domain:*.google.com.

Ctrl+F with the Network panel focused searches across every request's URL, headers and body. Search for a transaction ID, a click ID, or an email address you typed into a form — the last one is the quickest PII-leak check there is.

Read a hit#

Click a hit and open Payload. The query string holds the shared parameters; when gtag batches several events into one request, they arrive in the request body, one event per line. The payload reference explains every parameter. The ones you will read most:

  • tid: the measurement ID. A hit to the wrong property starts here.
  • en: the event name. ep.* and epn.* are its text and number parameters.
  • cid and sid: client and session IDs. A cid that changes mid-journey means the user was counted twice.
  • gcs: the Consent Mode state the hit was sent with.
  • dl and dt: the page URL and title GA4 will report.

The other tabs answer different questions:

  • Headers shows the full request URL and the response status.
  • Initiator shows the call stack that sent the request. When you see two page_view hits and have only one GA4 tag in GTM, the initiator tells you which script sent the second.
  • Cookies shows the cookies sent with the request and any set in the response. It is how you confirm a server-side GTM endpoint is setting first-party cookies on your domain.
The Initiator tab for a GA4 hit, showing the request call stack through gtag.js for G-JPEV636QX5 and the initiator chain from the page through two site bundles to the analytics.google.com collect request

The call stack and initiator chain behind one hit: the page loaded a site bundle, which loaded the gtag library, which sent the request.

In the panel settings (the gear at the right of the toolbar), Group by frame separates requests made inside iframes, which makes embedded checkouts and third-party widgets legible.

Change a hit's payload: edit and resend#

Sometimes reading a hit is not enough — you want to know what GA4 does with a different value. Chrome 156 can rebuild any fetch request as editable code:

  1. Right-click the hit → Edit and resend as fetch.
  2. The Console drawer opens with an await fetch(…) call that reproduces the request, ending in // Make any edits, then ENTER to resend.
  3. Change the URL parameters, then press Enter.

There is one trap. The generated call copies every header from the original request, including user-agent and Chrome's x-browser-* headers. Those turn the request into one that needs a CORS preflight, and analytics.google.com rejects the preflight — the resend fails with Failed to fetch, and the Network panel shows a 405 preflight next to a CORS error. Delete the headers block (or retype the call with just the URL, method: 'POST' and credentials: 'include') and the hit goes through with a 204.

A GA4 hit is right-clicked and Edit and resend as fetch is chosen; the Console drawer shows the generated fetch call; it is replaced with a shorter fetch whose tid points at a test property and whose event name is qa_resend_test; Enter returns 204 and the edited hit appears in the filtered Network log

Edit and resend as fetch, with the headers removed: the edited hit goes out and GA4 answers 204.

For fetch and XHR requests, the same menu also has plain Resend, which replays the request unchanged. Current gtag sends most GA4 hits with fetch, so both options usually appear. If a hit was sent another way and the menu does not offer them, right-click → Copy → Copy as fetch, paste it into the Console, and edit it the same way.

Every resend is a real hit. Point tid at a test property before you press Enter, never at production. Add _dbg=1 to the URL and the hit also appears in that property's DebugView.

Override content and headers#

Editing a hit changes one request. Local Overrides change what the server sends back, so the page itself behaves differently on every reload — on production, without a deploy.

  1. Right-click any request → Override content. The first time, DevTools asks for a folder: click Select folder, pick an empty folder, and click Allow when Chrome asks to let DevTools edit files in it.
  2. DevTools saves a copy of the response into that folder and opens it in Sources → Overrides.
  3. Edit the copy and press Ctrl+S.
  4. Reload. Chrome serves your copy instead of the live file until you untick Local overrides in Sources → Overrides, or delete the file.

A warning icon on the Network tab reminds you overrides are active, and Show all overrides in the same context menu lists them.

A page request is right-clicked and Override content is chosen; DevTools saves a copy of the HTML into the overrides folder and opens it in Sources; a script that pushes a qa_override event to the dataLayer is added and saved with Ctrl+S; after a reload the Console finds the qa_override event in the dataLayer

Override content on the page itself: add a dataLayer push, save, reload, and the production page now runs it.

Overrides change the response, not the request: you cannot use them to rewrite a GA4 hit directly. You change a hit by changing what feeds it — the page's HTML, a dataLayer.push in a site bundle, or the GTM container (gtm.js) itself.

They also only change what the server sent. If the page's scripts overwrite your edit after load, your edit loses. On the Merchandise Store, editing the <title> in an overridden HTML file does nothing to the dt parameter, because the site's framework sets document.title again once it loads. Edit the code that sets the value, not the markup it overwrites.

Override headers works the same way for response headers: right-click → Override headers, and the response headers in the Headers tab become editable. Remove or loosen a Content-Security-Policy header to find out whether CSP is what stops a tag from loading.

The Changes tab (Command Menu → "Show Changes") shows every edit you made as a diff. Paste it into the ticket and the developer sees exactly what you tested.

Simulate what real visitors experience#

Request blocking. Right-click a request → Block requests → Block request URL (or Block request domain for everything from that host), then reload. You see the site the way an ad-blocker user does, and find out whether your consent banner, checkout, or dataLayer depends on GTM loading.

The rules live in the Request conditions drawer (older versions call it Network request blocking). Current Chrome writes rules as URLPattern strings, such as *://www.googletagmanager.com/gtm.js?id=GTM-XXXX. The old *gtm.js* globs no longer parse, so let the context menu write the rule and edit it from there.

The Network panel filtered to googletagmanager.com/gtm.js showing the container request with status blocked:devtools, and the Request conditions drawer below with one enabled Block rule for the container URL marked 1 affected

GTM blocked from DevTools. The page still renders, but no container loads — the experience of every visitor whose ad blocker drops gtm.js.

Throttling. The throttling dropdown simulates slow connections, and Throttle requests in the same context menu slows down a single request — useful for seeing what happens when only the GTM container is slow. On Slow 4G, tags that fire on the click before a navigation are often lost.

Export and share a session#

  • HAR files. Right-click → Save all as HAR exports the whole session; Chrome strips cookies and auth headers by default, with a separate option to include them. More useful: drag a HAR file onto the Network panel to import it. When a client reports a bug you cannot reproduce, ask them for a HAR file and inspect their hits on your machine.
  • Copy as cURL or fetch. Right-click → Copy hands a developer the exact request. The same rule applies as for resending: every replay of a GA4 hit is a real hit.

Use cases#

The purchase hit reports zero revenue#

  1. Tick Keep log, filter to collect?v=2, and complete a test order.
  2. Find the hit whose en is purchase and open Payload.
  3. Read epn.value and cu. If value is missing or 0, the problem is upstream — check the purchase push in the Console against the ecommerce spec. If it is right here but wrong in reports, look at the property instead of the site.

Two page_views for every page load#

  1. Filter to collect?v=2 and reload.
  2. Open Initiator on each page_view hit.
  3. If one stack runs through gtm.js and the other through a hard-coded gtag/js?id=G-…, you have a duplicate snippet outside GTM. Sources will show you where it is loaded.
  1. Set throttling to Slow 4G and tick Keep log.
  2. Click an outbound link and come back.
  3. If the click event never appears, or shows as (canceled), the page navigated away before the hit was sent. That is a tag configuration problem, not a GA4 one.

Will checkout still work behind an ad blocker?#

  1. Right-click the gtm.js request → Block requests → Block request URL, and reload.
  2. Walk through checkout.
  3. If a button stops working or the consent banner never appears, the site depends on GTM loading. A real share of visitors will hit the same wall.

Test a new dataLayer push before the developer ships it#

  1. Right-click the page request → Override content.
  2. Add the push you have specified to the HTML copy, save, and reload.
  3. Use GTM Preview, or the filtered Network panel, to confirm the tag reads it and the hit carries the parameters you expect. Attach the Changes diff to the ticket.

How does GA4 treat a value it has never seen?#

  1. Right-click a real hit → Edit and resend as fetch.
  2. Delete the headers block, set tid to a test property, add _dbg=1, and change the parameter you are curious about — a currency code, a very long string, an unexpected type.
  3. Press Enter and watch it arrive in the test property's DebugView.

Where the Network panel stops#

The Network panel answers "what is leaving this browser right now?" It does not answer "is it still correct tomorrow?" A filter does not alert you when the purchase hit loses its value after next week's release, and nobody keeps DevTools open on every page of a site.

Next in the series: The Recorder for tracking QA — record a journey once and replay it after every release. Or go back to the overview.

How Tagfire helps#

  • Tag Debugger is the filtered Network panel without the filtering: every GA4 hit decoded as you browse — event name, parameters, consent state, client and session IDs — with duplicates flagged.
  • Monitors replay recorded journeys on a schedule and check that the hits you expect still fire with the parameters you expect, then email you when one does not. The documentation covers setup.
→Related reading