Proxy Pixel and Embed Through Your Own Domain
Use this guide if you want more reliable pixel and embed delivery in environments where ad blockers, privacy tools, or strict browser policies are common.
In the standard setup, the browser talks directly to:
cdn.affonso.iofor the pixel scriptapi.affonso.iofor tracking requestsaffonso.iofor the embedded dashboard
If that causes tracking or embed issues, you can proxy the same routes through your own domain. Affonso calls this first-party delivery.
If the hosted setup already works for you, keep using it. First-party delivery is optional.
What This Changes
With first-party delivery:
- the browser loads the pixel from your domain
- browser-visible tracking requests stay on your domain
- the embedded dashboard can load from your domain too
This usually helps because blockers are less likely to flag requests that start on your own domain.
Before You Start
You need:
- a domain you control
- a reverse proxy, rewrite layer, or edge function on that domain
- one path prefix that you will reserve for Affonso, such as
/r
You do not need to use /affonso. The prefix is configurable.
If your main goal is to avoid blockers, use a neutral prefix such as:
/rRoute 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 |
/r/embed/:path* | https://affonso.io/embed/:path* | Embedded dashboard |
Notes:
- keep the suffixes exactly as shown:
/pixel.js,/psl.min.js,/track,/signups, and/embed/... - only the prefix is your choice
- if you do not use signup tracking,
/r/signupscan be omitted - if you do not use the embedded dashboard,
/r/embed/:path*can be omitted
Step 1: Choose a Prefix
Pick one short path on your domain and use it consistently everywhere.
Examples:
/r/partner/links
All examples below use /r.
Step 2: 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"
},
{
"source": "/r/embed/:path*",
"destination": "https://affonso.io/embed/:path*"
}
]
}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));
}
if (url.pathname.startsWith('/r/embed/')) {
const upstream = new URL(
`https://affonso.io${url.pathname.replace('/r', '')}${url.search}`
);
return fetch(new Request(upstream.toString(), request));
}
return fetch(request);
},
};If you use Cloudflare Snippets or another Cloudflare proxy layer instead of a
Worker, keep the same path mapping and preserve the full request body for
POST /r/track and POST /r/signups.
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-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-Forwarded-For $proxy_add_x_forwarded_for;
}
location /r/embed/ {
proxy_pass https://affonso.io/embed/;
proxy_http_version 1.1;
proxy_set_header Host affonso.io;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}If your NGINX stack has extra request filtering, make sure it does not strip JSON request bodies or query strings on the tracking endpoints.
Step 3: 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.
If you also need consent mode, keep your existing consent attributes. Only the
script URL and data-api-base need to change.
Step 4: Update the Embedded Dashboard
If you use the embedded dashboard, load the iframe from the same prefix:
<iframe
src="https://customer.com/r/embed/referrals?token=YOUR_EMBED_TOKEN&theme=light&lang=en"
style="width: 100%; height: 600px; border: none;"
allow="clipboard-write"
></iframe>The embed token still needs to be created on your server. First-party delivery does not change token generation or auth requirements.
Verify the Setup
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. - If you use the embedded dashboard, confirm the iframe loads from
/r/embed/....
If all of those pass, the proxy is set up correctly.
Troubleshooting
The script loads, but clicks are not tracked
- Confirm the script tag uses your proxied
src - Confirm
data-api-basematches the prefix exactly - Check that
POST /r/trackreaches your proxy and returns200 - Make sure your proxy preserves request bodies and query strings
Signup tracking does not work
- Confirm you are proxying
/r/signups - Confirm your app still calls
window.Affonso.signup() - Check whether the referral cookie exists before signup
- Inspect the network request to
/r/signupsfor proxy errors
The embedded dashboard stays blank or fails to load
- Confirm you are proxying
/r/embed/:path* - Make sure the iframe
srcpoints to your domain, notaffonso.io - Confirm the embed token is created on the server and still valid
- Check browser DevTools for CSP, auth, or proxy errors
Can I proxy only part of the setup?
Yes.
Common combinations:
- pixel only:
/r/pixel.js,/r/psl.min.js,/r/track - pixel + signup tracking: add
/r/signups - embedded dashboard only:
/r/embed/:path*
FAQ
Is /affonso required?
No. The prefix is not hardcoded. Only the route suffixes are fixed.
Should I switch every customer to first-party delivery?
No. Use it when you have a concrete blocker problem or want a more resilient setup. The hosted setup remains the default.
Does this replace custom domains for the affiliate portal?
No. This guide is only about proxying the pixel and embedded dashboard through your own domain.


