Why this happens
Cloudflare's Google Tag Gateway is implemented as a Worker bound to a route pattern at the zone level. If the root domain's route is configured for example.com/* but a subdomain like shop.example.com sits on a different zone, or the pattern simply does not list it, requests from that subdomain never get proxied - they fall back to loading gtm.js directly from Google's own servers, so tag delivery there is not first-party even though the rest of the site is.
The opposite failure looks like a route that is too broad: if the pattern matches every path on every bound hostname, pages you actually want to exclude - a staging path, a page under a stricter Content-Security-Policy, a page already served through a different proxy - get swept into the gateway with no built-in opt-out. GTM's own trigger and tag logic has no visibility into this layer at all; the routing decision happens before a request ever reaches GTM's JavaScript, in the CDN or reverse-proxy configuration.
Fix it
- Audit which hostnames actually need first-party tag delivery - work from the property's live subdomain list, not an assumption that a root-domain rule covers everything under it.
- In Cloudflare, check the Worker route bound to the Google Tag Gateway integration and confirm it explicitly lists each subdomain that needs it (
www.example.com/*,shop.example.com/*) - a route does not cross zone boundaries even under the same Cloudflare account. - For a subdomain on a separate zone, add a second Worker route bound to that zone specifically; do not assume the primary domain's route extends to it.
- For page-level exclusion, add a more specific route that matches the excluded path and either passes the request straight through to origin unmodified, or add a path check inside the Worker script itself that skips the rewrite for that path.
- If self-hosting the proxy logic instead of using Cloudflare's built-in integration, apply the same exclusion rule in whatever layer is doing the URL rewrite (an nginx location block, a Fastly VCL condition, a custom Worker) - exclusion belongs in the proxy layer, not inside GTM.
- Redeploy and purge cache for every affected path - a stale cached response can make both a working exclusion and a broken one look identical during testing.
How to verify it worked
- Open the Network tab on a page that should be inside the proxy scope and confirm the tag-library request loads from your own domain (not
www.googletagmanager.com) and returns 200 with a JavaScript content type. - Load a page you deliberately excluded and confirm the opposite: the request goes straight to Google's own domain rather than being rewritten - if it is still proxied, the exclusion rule is not matching that path.
- Check for any custom header your Worker adds on a successfully proxied request, to rule out a stale edge-cached response masquerading as a fresh pass-through.
- Run GTM's own Preview mode on both an included and an excluded page and confirm tags fire identically - the routing change should be invisible to GTM's trigger and tag logic when implemented correctly.