Docs·Usage

Proxy guide

Ad blockers and privacy browsers stop requests to known analytics services, so some of your visitors are never counted. A proxy on your own domain makes the script's requests look like requests to your own site, which gets many of them through. This page explains what the api_host option moves and what it does not, what a proxy costs you, two setups that work, and how to check yours.

Read What you give up before you build anything. For some sites the costs outweigh the visitors you gain.

Why visitors with blockers go missing

Blockers work from filter lists: lists of hostnames and address patterns that belong to ads and analytics. When a request matches, the browser cancels it before it leaves, so nothing reaches Spreelytics and, as far as your numbers are concerned, the visit never happened. How many visitors this affects depends on who your visitors are. Technical audiences tend to use blockers more.

What api_host moves, and what it does not

Spreelytics.init({
  client_key: "YOUR_CLIENT_KEY",
  api_host: "https://www.example.com/sp",
});

With api_host set, every request the script sends goes to that address instead of https://sdk.spreelytics.com/1.0, with the same path on the end: https://www.example.com/sp/track/page, https://www.example.com/sp/track/event and so on. That covers the cookieless check, the install signal, pageviews, events, the end of each visit, pings and identify().

It does not move the script files. The loader comes from https://cdn.spreelytics.com/cdn/sdk/spreelytics-loader.js, and the loader always fetches the main script from that same host. That address is built into the loader, so serving the loader from your own domain does not change it. A blocker that blocks that host stops the script before api_host can help.

What you give up

Locations. Country, region, city, time zone and the position on the map all come from the network address that connects to Spreelytics's edge. Behind a proxy, that is your proxy's address, so every visitor appears to be wherever the proxy is. The location breakdowns, the map, the Realtime globe and country filters all show the proxy, not your visitors. Sending an X-Forwarded-For header does not help, because Spreelytics ignores it on purpose: anyone can write that header.

Data-center traffic is dropped. Requests from the IPv4 address ranges of AWS, Google Cloud, Oracle Cloud, DigitalOcean and Linode are dropped as data-center traffic. Behind a proxy, every request comes from the proxy, so a proxy running on one of these networks has all of its traffic dropped. That includes a proxy on Vercel, which runs on AWS, and one on an EC2 instance, a Google Cloud virtual machine, a DigitalOcean droplet or a Linode server. The requests still return 200, and nothing is recorded. Providers that are not on the list, such as Hetzner, OVH, Azure or Cloudflare, pass the check.

No cookieless tracking. On a cookieless audience, Spreelytics recognises visitors by a hash that includes their network address. Behind a proxy every visitor shares the proxy's address, so everyone on the same browser would count as one person. The cookieless dialog in Settings → Tracking script warns about this. Do not combine the two (see Cookieless tracking).

What keeps working: the device type, browser and operating system come from the User-Agent header, so they stay right as long as the proxy passes it through. Pages, referrers, UTM tags, channels, events, goals, revenue and identified people are all carried inside the requests, and are not affected.

What every proxy has to do

  1. Forward every request under your path to https://sdk.spreelytics.com/1.0/, keeping the rest of the path and the query string, for every method the script uses: GET, POST, PUT and OPTIONS.
  2. Connect to the upstream as sdk.spreelytics.com, both in the Host header and in the TLS handshake (SNI).
  3. Pass the User-Agent header through unchanged. If the proxy sends its own instead, requests can be dropped as coming from a tool or library, and the browser, operating system and device are lost.
  4. If the audience uses Allowed domains, pass the Origin and Referer headers through unchanged. Spreelytics reads the page's hostname from them.
  5. Pass the request body through unchanged.
  6. Do not forward your site's cookies. When the proxy is on your own domain, the browser attaches your site's cookies to the script's requests, login cookies included. Spreelytics does not need them, so strip the Cookie header.

Pick a path that nothing else on your site uses, such as /sp. Avoid names with "analytics" or "tracking" in them, which filter lists also look for.

nginx

On a server you run yourself, outside the networks listed above:

location /sp/ {
  proxy_pass https://sdk.spreelytics.com/1.0/;
  proxy_set_header Host sdk.spreelytics.com;
  proxy_ssl_server_name on;
  proxy_set_header Cookie "";
}
  • The trailing slashes matter. They make nginx replace /sp/ with /1.0/, so /sp/track/page goes to https://sdk.spreelytics.com/1.0/track/page.
  • proxy_ssl_server_name on makes nginx send the hostname in the TLS handshake (SNI), which sdk.spreelytics.com needs. nginx leaves it out by default.
  • proxy_set_header Cookie "" stops nginx from forwarding your site's cookies.
  • nginx passes User-Agent, Origin and Referer through by default, so leave them alone.

Then set api_host: "https://www.example.com/sp", with your own domain.

Cloudflare Worker

Cloudflare's network is not on the data-center list, so requests from a Worker are kept. Locations then show Cloudflare's network rather than your visitors.

export default {
  async fetch(request) {
    const url = new URL(request.url);
    const upstream = new URL(
      url.pathname.replace(/^\/sp/, "/1.0") + url.search,
      "https://sdk.spreelytics.com",
    );
    const proxied = new Request(upstream, request);
    proxied.headers.delete("Cookie");
    return fetch(proxied);
  },
};

Give the Worker a route such as www.example.com/sp/*, so that only the script's requests reach it, and set api_host: "https://www.example.com/sp". The Worker keeps the method, the body and the other headers of each request, User-Agent, Origin and Referer included.

Other proxies and framework rewrites

Any reverse proxy, or a rewrite rule in your framework or hosting platform, works the same way as long as it does everything listed above. Check where it runs: a rewrite runs on your host's servers, and on a host that runs on AWS, Google Cloud, Oracle Cloud, DigitalOcean or Linode, Vercel included, every proxied request is dropped.

Checking that it works

  1. Deploy the proxy, set api_host, and load a page of your site with the browser's Network tab open.
  2. Filter by your path, for example /sp/. You should see track/config, track/heartbeat and track/page going to your own domain with status 200, and no requests to sdk.spreelytics.com. The two script files still come from cdn.spreelytics.com.
  3. In Spreelytics, open or reload Settings → Tracking script. It should say "Last seen just now".
  4. Open Realtime. You should appear within a minute, placed where your proxy is rather than where you are. That is expected.

If the requests return 200 but "Last seen" does not move and Realtime stays empty, the proxy is almost certainly on a data-center network: move it. A 401 means the key is wrong, the Origin and Referer headers did not arrive, or your hostname is not in Allowed domains. A 400 means the proxy changed the request. Any other error usually means the path, the Host header or the TLS hostname is not set as described above.

See your own numbers today.

Free up to 100k events a month · no credit card

Contact

Tell us what you need.

A real person reads these. We usually reply within one working day.

Or email us directly at contact@spreelytics.com.