Proxy Pixel Tracking Through Your Own Domain
Use this guide when you want the Affonso tracking script and browser-visible tracking requests to stay on your own domain.
What This Covers
This guide proxies:
- the pixel script
- click tracking requests
- signup tracking requests
It does not proxy the embedded dashboard. That is covered separately in Proxy the Embedded Dashboard Through Your Own Domain.
Route Map
Proxy these customer-domain paths to the Affonso hosted endpoints:
| Your path | Forward to | Required for |
|---|---|---|
/r/pixel.js | https://cdn.affonso.io/js/pixel.min.js | Pixel installs |
/r/psl.min.js | https://cdn.affonso.io/js/psl.min.js | Pixel installs |
/r/track | https://api.affonso.io/v1/track | Click tracking |
/r/signups | https://api.affonso.io/v1/signups | Signup tracking |
Notes:
- keep the suffixes exactly as shown
- only the prefix is configurable
- if you do not use signup tracking,
/r/signupscan be omitted
Real Client IP
If you proxy /track or /signups, Affonso must still receive a trusted real
client IP header such as CF-Connecting-IP, True-Client-IP, or X-Real-IP.
If your proxy forwards only its own edge IP, attribution can still work while country data is wrong or missing.
Setup
You need:
- a domain you control
- a reverse proxy, rewrite layer, or edge function
- one reserved path prefix such as
/r
All examples below use /r.
Step 1: Add the Proxy Routes
Option 1: Vercel rewrites
If your site runs on Vercel, add these rewrites:
{
"rewrites": [
{
"source": "/r/pixel.js",
"destination": "https://cdn.affonso.io/js/pixel.min.js"
},
{
"source": "/r/psl.min.js",
"destination": "https://cdn.affonso.io/js/psl.min.js"
},
{
"source": "/r/track",
"destination": "https://api.affonso.io/v1/track"
},
{
"source": "/r/signups",
"destination": "https://api.affonso.io/v1/signups"
}
]
}Option 2: Cloudflare Worker
If you use Cloudflare, attach a Worker to a route such as:
customer.com/r/*The important part is that the Worker forwards the original request method, headers, body, and query string unchanged:
export default {
async fetch(request) {
const url = new URL(request.url);
const directMap = {
'/r/pixel.js': 'https://cdn.affonso.io/js/pixel.min.js',
'/r/psl.min.js': 'https://cdn.affonso.io/js/psl.min.js',
'/r/track': 'https://api.affonso.io/v1/track',
'/r/signups': 'https://api.affonso.io/v1/signups',
};
if (directMap[url.pathname]) {
return fetch(new Request(directMap[url.pathname], request));
}
return fetch(request);
},
};If you use Cloudflare Snippets or another Cloudflare proxy layer instead of a
Worker, preserve the full request body for POST /r/track and POST /r/signups
and keep CF-Connecting-IP or another trusted real-IP header intact.
Option 3: NGINX
If you use NGINX, add explicit locations for the Affonso routes:
location = /r/pixel.js {
proxy_pass https://cdn.affonso.io/js/pixel.min.js;
proxy_set_header Host cdn.affonso.io;
}
location = /r/psl.min.js {
proxy_pass https://cdn.affonso.io/js/psl.min.js;
proxy_set_header Host cdn.affonso.io;
}
location = /r/track {
proxy_pass https://api.affonso.io/v1/track;
proxy_http_version 1.1;
proxy_set_header Host api.affonso.io;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location = /r/signups {
proxy_pass https://api.affonso.io/v1/signups;
proxy_http_version 1.1;
proxy_set_header Host api.affonso.io;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}If your proxy stack has extra request filtering, make sure it does not strip
JSON request bodies or query strings on the tracking endpoints. If NGINX sits
behind another proxy, make sure $remote_addr is restored to the real visitor
IP first.
Step 2: Update the Pixel Script
Load the pixel from your domain and set data-api-base to the same prefix:
<script
async
defer
src="https://customer.com/r/pixel.js"
data-affonso="YOUR_PUBLIC_PROGRAM_ID"
data-cookie_duration="30"
data-api-base="/r"
></script>data-api-base must match the prefix from your proxy routes exactly.
If you also need consent mode, keep your existing consent attributes. Only the
script URL and data-api-base need to change.
Verify
After you deploy the proxy:
- Open
https://customer.com/r/pixel.jsdirectly in the browser and confirm it returns JavaScript. - Load a page with the updated pixel script.
- Visit your site with an affiliate parameter such as
?atp=test. - In DevTools, confirm the browser sends requests to
/r/track. - If you use signup tracking, confirm requests go to
/r/signups.
Troubleshooting
Clicks are tracked, but signups are not
- Confirm
data-api-basematches the prefix exactly - Confirm
POST /r/signupsis proxied if you use signup tracking - Make sure your proxy layer forwards the request body unchanged


