# Script proxy

> Serve the Datacenter script and its event endpoint from your own domain with a tiny Cloudflare Worker, so ad blockers drop fewer of your visits.

Source: https://datacenter.so/docs/script-proxy
Section: Start here

A script proxy serves the script and the event endpoint from your own domain. The browser only talks to your site, so blockers that match on our address let it through. Your numbers get closer to the real count.

The script proxy is on paid plans: Solo, Studio, Agency and Custom. Free does not include it.

## How it works

1. Your site loads the script from a path on your own domain, such as `/st/s.js`.
2. A small proxy on your domain fetches `https://datacenter.so/s.js` and returns it.
3. Events go to `/st/api/e` on your domain. The proxy forwards them to `https://datacenter.so/api/e`.

The script also loads `/st/x.js`, the [automatic events](https://datacenter.so/docs/automatic-events) add-on, from the same path. The snippets below pass it on. A proxy that does not still counts every visit; only the automatic events stop.

The site's **Integrations** page (under **Settings**) has each snippet below with your domain and token filled in, ready to copy.

## The visitor's address

Behind a proxy, the request that reaches Datacenter comes from your proxy's server. Without help, every visitor would look like the same address in the same country, and unique counts would be wrong.

So the proxy passes the visitor's real address in a header. Datacenter only believes that header when the proxy also sends your site's **proxy token**:

| Header | Value |
|---|---|
| `x-datacenter-proxy-key` | The site's proxy token |
| `x-datacenter-client-ip` | The visitor's IP address |
| `x-datacenter-client-country` | Optional. Two letters, such as `DE` |

Make the token on the site's **Integrations** page (under **Settings**), under **Script proxy**. Keep it in your proxy's settings. It never goes into a page, and the browser never sees it.

Why a token: anyone can type an `X-Forwarded-For` header, so Datacenter never reads it. Without a valid token, Datacenter uses the address its own network saw, which a browser cannot fake. A proxy without the token still works. It just counts every visitor from the proxy's own address. One address can send a site up to 600 events a minute, so a busy site behind a proxy needs the token, or events past that are not counted.

If the token leaks, press **Make a new token** and update your proxy. The old one stops working at once.

## Cloudflare Worker

Create a Worker on a route of your site, such as `shop.example.com/st/*`. Add the token as a secret named `PROXY_KEY` (`npx wrangler secret put PROXY_KEY`).

```js
// Datacenter script proxy. Route: <your domain>/st/*
export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    const path = url.pathname.replace(/^\/st/, '');
    if (path === '/s.js' || path === '/sa.js' || path === '/x.js') {
      return fetch('https://datacenter.so' + path + url.search, { cf: { cacheTtl: 300 } });
    }
    if (path === '/api/e' && request.method === 'POST') {
      const headers = new Headers(request.headers);
      headers.set('x-datacenter-proxy-key', env.PROXY_KEY);
      headers.set('x-datacenter-client-ip', request.headers.get('cf-connecting-ip') || '');
      headers.set('x-datacenter-client-country', request.cf?.country || '');
      return fetch('https://datacenter.so/api/e', { method: 'POST', headers, body: request.body });
    }
    return new Response('Not found', { status: 404 });
  },
};
```

## Next.js on Vercel

A plain rewrite in `next.config.js` works, but cannot add headers, so every visitor counts from Vercel's address. Use middleware instead. Set `DATACENTER_PROXY_KEY` in the project's environment variables.

```ts
// middleware.ts
import { NextResponse, type NextRequest } from 'next/server';

export const config = { matcher: '/st/:path*' };

export function middleware(req: NextRequest) {
  const path = req.nextUrl.pathname.replace(/^\/st/, '');
  const target = new URL(path + req.nextUrl.search, 'https://datacenter.so');
  const headers = new Headers(req.headers);
  if (path === '/api/e') {
    headers.set('x-datacenter-proxy-key', process.env.DATACENTER_PROXY_KEY ?? '');
    headers.set('x-datacenter-client-ip', (req.headers.get('x-forwarded-for') ?? '').split(',')[0].trim());
    headers.set('x-datacenter-client-country', req.headers.get('x-vercel-ip-country') ?? '');
  }
  return NextResponse.rewrite(target, { request: { headers } });
}
```

Vercel sets `x-forwarded-for` itself, so here it is safe to read.

## nginx

Inside your `server { }` block:

```nginx
location = /st/s.js  { proxy_pass https://datacenter.so/s.js;  proxy_set_header Host datacenter.so; proxy_ssl_server_name on; }
location = /st/sa.js { proxy_pass https://datacenter.so/sa.js$is_args$args; proxy_set_header Host datacenter.so; proxy_ssl_server_name on; }
location = /st/x.js  { proxy_pass https://datacenter.so/x.js$is_args$args;  proxy_set_header Host datacenter.so; proxy_ssl_server_name on; }
location = /st/api/e {
  proxy_pass https://datacenter.so/api/e;
  proxy_set_header Host datacenter.so;
  proxy_ssl_server_name on;
  proxy_set_header x-datacenter-proxy-key "YOUR_PROXY_TOKEN";
  proxy_set_header x-datacenter-client-ip $remote_addr;
}
```

If nginx sits behind a load balancer, set `$remote_addr` from the real client with the `real_ip` module first.

## The tag

Point the tag at your own path, and tell the script where to send events with `data-api`.

```html
<script defer data-site="shop.example.com" data-api="/st/api/e" src="/st/s.js"></script>
```

In [90-day tracking](https://datacenter.so/docs/tracking-modes) the tag loads `sa.js` instead:

```html
<script defer data-site="shop.example.com" data-mode="attribution" data-api="/st/api/e" src="/st/sa.js?d=shop.example.com"></script>
```

## Check it

Open your site, then the site's dashboard. The visit shows within a few seconds, with your own country. If every visit shows the same country, the token or the address header is missing.
