# First-party Tag Gateway

The Tag Gateway is a small proxy on a subdomain of your own site that carries the entire tracker surface through to AdPix. The tag and the beacon become first-party, cookies are written again, and ad-blocker lists have nothing to match.

## What the gateway changes

By default the tracker file loads from the AdPix CDN host and the beacon goes to the AdPix collector host. Both are hosts outside your site: ad-blocker lists know them, and the cookie the collector wanted to write is a third-party cookie.

With the Tag Gateway, a subdomain of your own domain — `metrics.<your domain>` by default — sits in front of all that traffic and proxies it to AdPix.

| Subject | Direct tag | Tag Gateway |
| --- | --- | --- |
| Tracker file host | the AdPix CDN host | your own subdomain |
| Beacon target | the AdPix collector host | the same subdomain of yours |
| Collector cookie | not written (third-party) | written (first-party) |
| Ad-blocker loss | around 25% | around 3–8% |
| What you have to do | just the snippet | a CNAME record and a reverse proxy |

Identity works in both modes; in direct mode it rides the durable ID the tracker keeps in local storage. What the gateway adds is longer persistence and a much smaller blocked surface.

> **The gateway does not displace the direct tag**
>
> Generating a bundle turns nothing off. The install instructions then show two snippets — direct and gateway — and you put one of them on the page. Until that moment your site sends data exactly as before.

## What you need

- Access to the domain's DNS, for one CNAME record.
- A reverse proxy holding a TLS certificate for that subdomain. The console generates ready configs for Caddy, nginx and a Cloudflare Worker.
- Editor level or above on the property — generating a bundle mints a credential, so it is protected like any property-settings change. Viewing an existing bundle only needs report-read access.

## Generate the bundle

1. In the console open **Admin** → **Property settings** → **Data streams**, then open the stream.
2. Open the **Tag-Gateway (first-party)** section. The default subdomain is `metrics` and you can change it; changing it rebuilds the bundle and mints a new proxy token.
3. Generate the bundle. The output carries the DNS record, the proxy token, the gateway snippet, a health URL and three proxy configs.
4. Add the CNAME record exactly as printed in the bundle — your subdomain pointing at the AdPix gateway host.
5. Put one of the three configs on your proxy and reload it. Wait for the TLS certificate for that subdomain to issue.
6. Replace the direct snippet in your site's `<head>` with the gateway snippet.
7. Run the gateway diagnostic from the same section.

> Generating the Tag-Gateway bundle: the DNS record and the Caddy/nginx/Cloudflare config. — [analytics.adpix.io/en/settings](https://analytics.adpix.io/en/settings)

Each data stream's bundle is stored on the stream itself — the token is kept encrypted and the bundle is re-rendered every time you open the section. So you do not have to keep a copy anywhere, and you never have to regenerate just to read it again. The standalone wizard under **Settings** behaves the older way: it shows the token once.

## The paths that must be proxied

This is the most important part of the page. The generated config carries a strict allowlist, and anything outside it gets a `404`. The list is the **entire** tracker surface, not just the tracker file and the beacon:

| Path | What travels over it |
| --- | --- |
| `/t.js` and `/t/*` | the base tracker file |
| `/collect` | the main event-send path |
| `/i` | the single-pixel fallback |
| `/id` | an alias of the event-send path |
| `/apx/*` and `/sov/*` | stream config: enhanced measurement, cross-domain domains, heatmap, the consent block |
| `/consent.js` | the first-party consent banner |
| `/consent/*` | the full banner config and the consent-record write |
| `/fpx.js` | the fingerprint-enrichment bundle |
| `/hm.js`, `/hm` and `/hm/*` | the heatmap recorder, its samples and the page-structure snapshot |
| `/_apx_health` and `/_sov_health` | proxy health checks |

> **A missing path breaks things silently**
>
> If a path is not in the allowlist your proxy answers it with a `404`, and nothing is recorded as an error in the AdPix console. The consequences: without `/consent/*` the consent banner never renders on your domain and no consent is recorded; without `/apx/*` the tracker falls back to the default enhanced-measurement settings; without `/hm*` heatmaps collect no samples. Copy the config from the bundle and do not shorten the path list.

The Caddy, nginx and Cloudflare Worker versions are rendered from one source, so all three carry exactly this list. If you run a different proxy, port the whole list to it.

## The headers the proxy adds

The generated config sets four headers on every request:

| Header | What it does |
| --- | --- |
| `X-Site-Key` | tells the collector this request came through a customer gateway |
| `X-Site-Token` | the credential that authenticates that gateway |
| `X-Forwarded-For` | the visitor's real IP address |
| `X-Real-IP` | the same, for proxies that read this one |

Take the last two seriously: without them the collector sees your proxy server's IP, and country, city and network collapse to one value for every visitor. The Caddy and nginx configs set all four; the Cloudflare Worker sets only the two `X-Site-*` headers, because Cloudflare already forwards the visitor's address itself.

Any request carrying `X-Site-Key` must also carry a valid `X-Site-Token`, or the answer is a `403`. Direct-mode requests, which do not carry that header, are unaffected.

## Rotating the token with no downtime

The proxy token is a credential and sooner or later has to change — after someone leaves, after a config leak, or simply as periodic hygiene. Only an argon2id hash of it is stored.

The detail that makes rotation painless: the collector accepts **any** non-revoked token for that property. Several tokens can be valid at once.

1. Generate a new bundle. A new token is minted and the old one is still valid.
2. Deploy the proxy config with the new token and confirm real traffic is going through it.
3. Only now revoke the old token.

Listing and revoking happen through the API:

```bash
curl 'https://api.adpix.io/api/v1/proxy-tokens?site=<property_id>' \
  -H 'Cookie: ap_admin=…'
```

```json
{
  "site_id": "…",
  "tokens": [
    { "prefix": "9f2a1c40", "created_at": "…", "revoked_at": "",  "active": true  },
    { "prefix": "3b7e0d55", "created_at": "…", "revoked_at": "…", "active": false }
  ]
}
```

```bash
curl -X DELETE 'https://api.adpix.io/api/v1/proxy-tokens?site=<property_id>&prefix=3b7e0d55' \
  -H 'Cookie: ap_admin=…'
```

The list shows only each token's eight-character prefix, never the value itself; that prefix is what revoking takes. Listing needs report-read access; revoking needs the property-settings edit capability.

> **Do not swap the order**
>
> Revoke the old token before the new config is deployed and your proxy gets a `403` on every request until the deploy lands — with no data collected in the gap. Revocation is the last step, never the first.

## Diagnosing it

The same **Tag-Gateway** section runs a diagnostic. Four checks run in order, each with its own remediation:

| Check | What it means | If it fails |
| --- | --- | --- |
| DNS | the subdomain resolves to an address | add or fix the CNAME record |
| Proxy health | `/_sov_health` on the subdomain answers `200` | the proxy is down, or the TLS certificate for that subdomain is not valid |
| Tag served | `/t.js` through the proxy really returns tracker bytes | the proxy is not forwarding the path, or not setting the bundle's headers |
| Data flowing | events arrived from that domain in the last 24 hours | the gateway snippet is not on the page yet, or nobody has loaded a page |

The first three are diagnostic — they explain why data is not arriving. The fourth is definitive: the stream is not marked verified until a real event lands.

The stream's own status shows the same thing and has five states: awaiting install, proxy live, tag found in the page source, receiving data, and error. Only "receiving data" means you are done.

You can also call the health URL yourself; it is printed in the bundle, and a `200` means the proxy is alive and reaching AdPix.

## After it is live

- **Swap the snippet.** While your page still loads the direct tag, the gateway is up but no traffic goes through it.
- **Keep one install path.** Do not load the tag from both your site template and a tag manager.
- **Re-run the diagnostic after every proxy change.** A Caddy or nginx upgrade sometimes loses the path list, and the first symptom is an empty consent banner or an empty heatmap — not a drop in page views.

The headers and cookies of this path are listed in [Cookies and headers](analytics/developers/cookies-and-headers), and the exact shape of the send envelope is in [The collect envelope](analytics/developers/collect-envelope).

## Frequently asked questions

### Does generating a gateway bundle turn off the direct tag?

No. The direct tag stays available and the install instructions show both snippets side by side. The Tag Gateway is an addition, not a replacement; nothing changes until you swap the snippet on your page.

### What happens if I leave one path out of the proxy config?

That feature silently returns a `404` on your own domain and nothing is logged in the AdPix console. Drop `/consent/*` and the consent banner never appears; drop `/apx/*` and the tracker falls back to default enhanced-measurement settings; drop `/hm*` and heatmaps record nothing. Copy the generated config as-is.

### Does rotating the proxy token cause downtime?

Not if you keep the order. The collector accepts any non-revoked token for the property, so the new and the old one are valid at the same time. Generate the new bundle, deploy the proxy with the new token, and revoke the old one last.

### Why are cookies written through the gateway but not in direct mode?

On the direct cross-origin path the collector's cookie would be a third-party cookie that browsers block or partition, so AdPix deliberately writes none there and identity rides the durable local-storage ID. A request coming from your gateway carries `X-Site-Key`, counts as first-party, and gets its cookies back.

## Related

- [Data streams](https://docs.adpix.io/en/analytics/collect/data-streams/)
- [Install the measurement tag](https://docs.adpix.io/en/analytics/start/install-the-tag/)
- [Cookies and headers](https://docs.adpix.io/en/developers/analytics/developers/cookies-and-headers/)
- [The collect envelope](https://docs.adpix.io/en/developers/analytics/developers/collect-envelope/)

---

[Docs](https://docs.adpix.io/en/analytics/collect/tag-gateway/) · AdPix
