Track

Ad blockers and proxies

Serve the tag and endpoint from your own domain so ad blockers don't drop events.

Many ad blockers and privacy extensions block requests to analytics services, which can hide a good share of your website visits. The fix is to serve the script and send events through your own domain. To the browser they're then part of your site, and blockers leave them alone.

This mostly matters for websites and web apps. Events sent from servers, and from games and apps outside a browser, don't go through browser extensions.

How it works

You forward two paths on your domain to AnyAnalytics:

  • /aa/script.js to https://api.anyanalytics.org/script.js
  • /aa/batch to https://api.anyanalytics.org/v1/batch

Then you load the script from the first path and point it at the second. The examples use /aa, but any path works. Pick one without words like "analytics" or "track", which blockers look for.

Set it up

  1. Forward the two paths

    Pick your hosting below and add the config to your site.

  2. Load the script from your domain

    Replace your tag with this one. data-api sends events to your own path.

index.htmlhtml
<script defer src="/aa/script.js" data-api="/aa/batch" data-key="vxw_YOUR_WRITE_KEY"></script>

Next.js

next.config.jsjs
module.exports = {
  async rewrites() {
    return [
      { source: "/aa/script.js", destination: "https://api.anyanalytics.org/script.js" },
      { source: "/aa/batch", destination: "https://api.anyanalytics.org/v1/batch" },
    ];
  },
};

Vercel

vercel.jsonjson
{
  "rewrites": [
    { "source": "/aa/script.js", "destination": "https://api.anyanalytics.org/script.js" },
    { "source": "/aa/batch", "destination": "https://api.anyanalytics.org/v1/batch" }
  ]
}

Netlify

netlify.tomltoml
[[redirects]]
  from = "/aa/script.js"
  to = "https://api.anyanalytics.org/script.js"
  status = 200
  force = true

[[redirects]]
  from = "/aa/batch"
  to = "https://api.anyanalytics.org/v1/batch"
  status = 200
  force = true

Cloudflare Worker

worker.jsjs
// Route this worker to example.com/aa/*
const API = "https://api.anyanalytics.org";

export default {
  async fetch(request) {
    const url = new URL(request.url);
    if (url.pathname === "/aa/script.js") {
      return fetch(API + "/script.js", { cf: { cacheTtl: 3600 } });
    }
    if (url.pathname === "/aa/batch") {
      const upstream = new Request(API + "/v1/batch", request);
      upstream.headers.delete("cookie");
      upstream.headers.set("X-Forwarded-For", request.headers.get("CF-Connecting-IP") ?? "");
      return fetch(upstream);
    }
    return fetch(request);
  },
};

nginx

nginx.confnginx
# Inside your site's server { ... } block
location = /aa/script.js {
    proxy_pass https://api.anyanalytics.org/script.js;
    proxy_set_header Host api.anyanalytics.org;
    proxy_ssl_server_name on;
}

location = /aa/batch {
    proxy_pass https://api.anyanalytics.org/v1/batch;
    proxy_set_header Host api.anyanalytics.org;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Cookie "";
    proxy_ssl_server_name on;
}

With the JavaScript SDK

If you installed the SDK from npm, the script is already part of your bundle. Only forward the batch path, and set endpoint instead of host:

analytics.jsjs
analytics.init({
  writeKey: "vxw_YOUR_WRITE_KEY",
  endpoint: "/aa/batch", // a path on your own domain
  capturePageviews: true,
});

Check that it works

Open your site with an ad blocker on and look at the browser's network tab. Requests should go to /aa/script.js and /aa/batch on your own domain and succeed. Your visit should show up in Live events within a few seconds.

Locations and IP addresses

Behind a proxy, the request reaches us from your proxy's address instead of the visitor's. Locations and IP exclusion need the visitor's real IP address.

  • The Cloudflare Worker and nginx configs pass it on in the X-Forwarded-For header. We only accept that header from proxy addresses we trust.
  • Next.js, Vercel and Netlify rewrites run on serverless platforms with no fixed addresses, so the visitor's IP can't be passed on reliably. Visits sent this way are counted without a location, and IP exclusion can't apply to them.

When we can't tell where a visitor is, the visit is counted with no location rather than your proxy's location. Everything else (pageviews, sources, devices, events) works the same.

Note

Because the batch path is on your own domain, the browser sends your site's cookies to it. The Cloudflare Worker and nginx configs remove them before forwarding.