Why does my server-side GTM custom domain return a 404 instead of routing to the container?
A 404 on your server-side custom domain almost always means the domain's DNS or load balancer is reaching your server container infrastructure, but the request path or Host header does not match what the container's App Engine or Cloud Run service expects. Check that the domain mapping actually points at the correct service, and that you are hitting the container's client endpoint path, not a bare root path it does not handle.
Why this happens
A custom domain for server-side GTM is a routing setup: your DNS points a subdomain at your server container's hosting infrastructure, and that infrastructure has to correctly forward the request to the running container instance with the right path intact. A 404 specifically (not a timeout, not a connection refused) means something in that chain is responding - it is just responding "not found" because either the domain mapping is misconfigured, the load balancer's URL map does not route your path to the right backend, or you are requesting a path the container genuinely does not serve.
Fix it
- Confirm the request is reaching your infrastructure at all: check Cloud Run or App Engine request logs for your test request's timestamp - if nothing appears, the 404 is coming from DNS/CDN/load balancer level before it reaches your container.
- If the request does appear in your container's logs with a 404, check the exact path requested against your server container's expected client-side paths - most setups expect paths like
/gtm.jsor/g/collect, not a bare domain root. - Verify the domain mapping itself with
gcloud run domain-mappings describe --domain=yourdomain.com- it should show the mapping resolved and pointing at the correct service and region. - If you are using a load balancer in front of Cloud Run, check its URL map has a path matcher that forwards your domain's traffic to the server GTM backend service.
- Confirm the server container itself is actually running - a stopped, crashed, or never-successfully-deployed revision can still resolve DNS correctly but serve a platform-level 404 rather than your container's own response.
How to verify it worked
Run curl -I https://your-custom-domain.com/gtm.js from a terminal (not a browser, to avoid caching) and confirm a 200 with a JavaScript content type once fixed. Cross-check the same request's timestamp appears in your Cloud Run or App Engine request logs with a 200 status, confirming DNS, load balancer, and container are all connected end to end.
Still stuck after working through this?
Send us what you are seeing in Preview and the Network tab. We trace GTM containers for a living and can usually tell you what is actually happening in one look.
Ask a GTM Engineer