Blog
When your tagging server proxies to an upstream, the upstream often needs to know the original hostname.
When your sGTM container forwards a request to an upstream service (analytics, webhook, internal API), the upstream sees the request coming from your tagging server's IP, not the original visitor's. Some upstreams need to know the original hostname for routing, logging, or rate limiting. Pass it as a custom header.
| Header | What it carries |
|---|---|
| X-Forwarded-Host | Original Host header from the browser. |
| X-Forwarded-For | Original client IP (if you forward IP). |
| X-Forwarded-Proto | https or http. |
| X-Original-User-Agent | Original UA, in case the upstream replaces it. |
| X-Real-IP | Some legacy proxies prefer this name. |
const originalHost = getRequestHeader('host');
const originalIP = getRequestHeader('x-forwarded-for');
const originalUA = getRequestHeader('user-agent');
sendHttpRequest('https://upstream.internal/ingest', {
method: 'POST',
body: getRequestBody(),
headers: {
'Content-Type': 'application/json',
'X-Forwarded-Host': originalHost,
'X-Forwarded-For': originalIP,
'X-Original-User-Agent': originalUA
}
});
Some destinations (especially internal services behind a load balancer) rewrite the Host header when receiving a request. If your upstream service uses Host-based routing, you must use a custom header instead of relying on Host. X-Forwarded-Host is the conventional name.
If your upstream is third-party (a vendor's API, not your own), think about whether you really want to forward the original IP. Most ad platforms accept it (Meta CAPI uses it for matching), but for non-essential destinations, anonymise or strip it before forwarding.
If the upstream is your own service, log the incoming headers for the first few requests after deploying the change. Confirm X-Forwarded-Host matches what you expect. The most common bug is that GTM strips or renames headers automatically; the actual values that arrive at the upstream are sometimes not what your template specified.