Ad blockers filter known tracking domains, and api.pixelrelay.io appears on some lists. A browser POST straight to it fails for a share of visitors.
The proxy route avoids that. The browser posts to your own domain, and your Hydrogen server passes the event on to PixelRelay.
Browser → POST /api/relay (your domain, first-party)
→ Hydrogen server → POST https://api.pixelrelay.io/events/pixel
Why this isn't CNAME cloaking
There's no PixelRelay script to blocklist. The client is compiled into your own bundle as a normal npm import, so there's no <script src="...pixelrelay.io/pixel.js"> for a filter list to match. The hop to PixelRelay happens inside your server runtime, where no blocker, DNS filter or browser extension can see it. You choose the proxy path with the proxyUrl prop, so it isn't a fixed fingerprint either.
Vendor tags are the exception. fbevents.js, gtag.js and the rest load from their own CDNs and will be blocked for some visitors. They only exist to set match-quality cookies. Blocked sessions are still fully covered server-side.
Add the route
npx pixelrelay-hydrogen install-route
This writes app/routes/api.relay.tsx. You can also copy the template from the @pixelrelay/hydrogen package. Then set proxyUrl="/api/relay" on <PixelRelayProvider>.
/api/relay needs package version 1.3 or later. Earlier versions install api.pixel.tsx at /api/pixel, which is the provider's default and keeps working through 1.x.
What the route does
- Rejects anything that isn't JSON (
415). - Reads
PIXEL_RELAY_SECRETfrom the server environment. It never comes from the browser. If it's missing, the route returns500. - Adds
Authorization: Bearer <PIXEL_RELAY_SECRET>. - Forwards the visitor's IP as
X-Real-Client-IP, taken fromx-forwarded-fororx-real-ip, for location matching. - POSTs to PixelRelay and returns
204. If PixelRelay refuses, you get its status back (401,402,429). If it can't be reached,502.
The route's loader also serves your connected platform IDs and Google Ads labels back to the provider, which is why you don't hardcode pixel IDs. It also hands the browser a short-lived signed token that the checkout pixel uses, so the long-lived secret never leaves your server.
Environment variables
| Variable | Required | Where to get it |
|---|---|---|
PIXEL_RELAY_SECRET | Yes | PixelRelay app → Storefront guide (after Hydrogen mode is on under Settings → Storefront) |
PIXEL_RELAY_URL | No | Built into the package. Only for staging or self-hosted use |
Verify
Browser: open the Network tab, filter on relay, navigate, and look for POST /api/relay returning 204.
Ad blocker: turn on uBlock Origin or Privacy Badger. The requests should still go through.
curl:
curl -i -X POST https://your-store.com/api/relay \
-H "Content-Type: application/json" \
-d '{"shop":"your-store.myshopify.com","eventName":"connection_test"}'
connection_test checks your token, shop and plan without creating an event, and should return 204. 401 means a wrong token, 402 means the plan doesn't include Hydrogen.
Security
PIXEL_RELAY_SECRET is created for each store when you turn on Hydrogen mode.
- It's never sent to the browser. Only
api.relay.tsxreads it. - PixelRelay stores it encrypted and compares it in constant time.
- PixelRelay only trusts
X-Real-Client-IPon requests that carry a valid token.
Rotate the token
- In the PixelRelay app, open Storefront guide and select Rotate Token.
- Update
PIXEL_RELAY_SECRETin Oxygen and locally.
Events return 401 until the variable is updated, so rotate during low traffic.
Where it shows in the app
Events that arrive through this route with a valid token are labelled hydrogen. They feed the Hydrogen event stream card on Home, which shows Receiving events, the event count for the last 7 days, and the last event time. Checkout and order events arrive by other routes and aren't counted there.
Need help? Email [email protected]