DocsStart here

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.

On this page

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 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:

HeaderValue
x-datacenter-proxy-keyThe site's proxy token
x-datacenter-client-ipThe visitor's IP address
x-datacenter-client-countryOptional. 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).

JavaScript
// 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.

TypeScript
// 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 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.

Something missing or wrong? Email hello@datacenter.so.