# Proxy the script through your domain

> Serve the Revfaro script and its event endpoint from your own domain so ad blockers don't drop your visitors, with Next.js, Vercel, nginx or Cloudflare Workers.

**In short:** Some ad blockers stop analytics scripts loaded from another domain. Forward two paths on your own domain to Revfaro (the script and the event endpoint), point the script at them with `data-api`, enable First-party proxy in Tracking settings, and let your authenticated proxy pass on the visitor's `User-Agent` and IP address. Payments are never affected by ad blockers, because they arrive by webhook from your payment provider.

Ad blockers keep lists of analytics domains and block scripts loaded from them. When the script and the endpoint it sends to live on **your own domain**, most blockers let them through.

## How the proxy works

You forward two paths on your domain to Revfaro:

| Path on your site  | Forwards to            |
| ------------------ | ---------------------- |
| `/stats/script.js` | `https://revfaro.com/js/script.js` |
| `/stats/event`     | `https://revfaro.com/api/event`    |

You can pick any path. Neutral names such as `/stats/` work better than words blockers look for, like `analytics` or `tracking`.

Events then reach Revfaro from your server. The proxy must send:

- the **`User-Agent`** header, and
- the visitor's **IP address**, in `X-Revfaro-Visitor-IP`, `X-Vercel-Forwarded-For`, or `X-Forwarded-For` (in that order), and
- your site's **proxy key**, in `X-Revfaro-Proxy-Secret`.

Revfaro needs the address for IP exclusions, cookieless visitors and its per-visitor limits, and never stores it.

**Location reporting:** Revfaro only trusts Cloudflare location information for the connecting visitor. When your proxy forwards a visitor's address, Cloudflare sees the proxy, so Revfaro leaves the visitor's location unknown. Vercel location headers are not used. For location reporting, send events directly to Revfaro's Cloudflare-protected hostname.

## Set up the proxy

In **Website settings → Tracking**, enable **First-party proxy** and copy the proxy key into a server-only environment variable named `REVFARO_PROXY_SECRET`. Never put it in HTML, browser code, or a `NEXT_PUBLIC_` variable. Without the matching key, forwarded IP headers are ignored and the sender gets the direct-visitor limit.

**Next.js on Vercel**

Use a rewrite for the script and a server route for events. A plain event rewrite cannot add your private proxy key. On Vercel, read its platform-provided `X-Vercel-Forwarded-For` header; do not copy an untrusted browser header as the visitor IP.

```js next.config.js
module.exports = {
  async rewrites() {
    return [{ source: '/stats/script.js', destination: 'https://revfaro.com/js/script.js' }];
  },
};
```

```js app/stats/event/route.js
export async function POST(request) {
  const response = await fetch('https://revfaro.com/api/event', {
    method: 'POST',
    headers: {
      'Content-Type': 'text/plain',
      'User-Agent': request.headers.get('user-agent') ?? '',
      'X-Vercel-Forwarded-For': request.headers.get('x-vercel-forwarded-for') ?? '',
      'X-Revfaro-Proxy-Secret': process.env.REVFARO_PROXY_SECRET,
    },
    body: await request.text(),
  });
  return new Response(null, { status: response.status });
}
```

**nginx**

For nginx that receives your visitors' connections itself. Behind another proxy or load balancer, pass the address it resolved instead of `$remote_addr`.

```nginx
location = /stats/script.js {
    proxy_pass https://revfaro.com/js/script.js;
    proxy_ssl_server_name on;
}
location = /stats/event {
    proxy_pass https://revfaro.com/api/event;
    proxy_ssl_server_name on;
    proxy_set_header X-Revfaro-Visitor-IP $remote_addr;
    proxy_set_header X-Revfaro-Proxy-Secret "YOUR_SERVER_ONLY_PROXY_KEY";
    proxy_set_header Cookie "";
}
```

**Cloudflare Workers**

A Worker on your site's route, in front of your origin. Store `REVFARO_PROXY_SECRET` as a Worker secret:

```js worker.js
export default {
  async fetch(request, env) {
    const { pathname } = new URL(request.url);
    if (pathname === '/stats/script.js') return fetch('https://revfaro.com/js/script.js');
    if (pathname === '/stats/event') {
      return fetch('https://revfaro.com/api/event', {
        method: 'POST',
        headers: {
          'Content-Type': 'text/plain',
          'User-Agent': request.headers.get('User-Agent') ?? '',
          'X-Revfaro-Visitor-IP': request.headers.get('CF-Connecting-IP') ?? '',
          'X-Revfaro-Proxy-Secret': env.REVFARO_PROXY_SECRET,
        },
        body: await request.text(),
      });
    }
    return fetch(request);
  },
};
```

Then change your script tag: load the script from your domain and send events there with `data-api`:

```html
<script defer data-website-id="YOUR_SITE_ID" data-api="/stats/event" src="/stats/script.js"></script>
```

Deploy, open your site and check that a request to `/stats/event` returns `202` in your browser's Network tab. [Verify your installation](https://revfaro.com/docs/verify-installation/) has the details.

> **Always set data-api**
> Without `data-api`, a script served from your domain sends its events to `/api/event` on your domain, which doesn't exist.

## Limits behind a proxy

Every visitor behind your authenticated proxy gets the same limit as a visitor who connects directly: 1,200 events every 10 minutes per website. On top of that, one proxy address can forward up to 30,000 events every 10 minutes for one website.

If your proxy doesn't pass the visitor's address, everything arrives from your server's address: IP exclusions can't match visitors, cookieless mode counts everyone with the same browser as one visitor, and a busy site reaches the per-address limit quickly. Always forward the correct address; a proxy server's location is not a visitor's location.

## Serve the cookieless script

The same setup works for [cookieless mode](https://revfaro.com/docs/cookieless/): point the script path at `https://revfaro.com/js/script.cookieless.js` and keep everything else as it is. Keep the authenticated event server route; a script rewrite alone does not forward your visitors' addresses.

For self-hosted Revfaro behind Coolify, the deployment proxy must also preserve and validate the sender address. See the deployment guide: Traefik can replace forwarded headers by default. A site proxy key does not configure your infrastructure proxy.

## Frequently asked questions

### Do ad blockers affect revenue tracking?

Payments themselves are always recorded, because Stripe, Dodo Payments, Lemon Squeezy and Polar send them to Revfaro by webhook. What a blocked script costs you is the visit, so the payment may not be linked to a source.

### Why does my proxy need to pass on the visitor's IP address?

Revfaro uses it for IP exclusions, cookieless visitors and its per-visitor limits, and never stores it. Without it, events arrive from your server's address and all your visitors look like one visitor there. Forwarded visits have an unknown location.

### Can I proxy the cookieless script?

Yes. Point the script path at `/js/script.cookieless.js` instead. Make sure your proxy passes on the visitor's IP address, because cookieless mode recognizes visitors by it.

---

Source: https://revfaro.com/docs/proxy/ · Last updated 2026-11-02
