Aller au contenu
snorklee
Trafic IA Analytics Tarifs Manifeste Documentation Contact Connexion Essai gratuit 14 jours

Serve the Snorklee code from your own domain (anti-blocking)

Chrome 121+, uBlock Origin, Brave Shields, AdGuard and most ad blockers now block, by default, any call to an outside domain labelled "analytics" — even when the service runs no ads and no advertising tracking, which is our case.

If you do nothing, you currently lose 5–20% of your visits, and that share will climb to 30–50% within 12–24 months, as Chrome extends Tracking Protection to every session (not just incognito).

The fix: serve the Snorklee code and the API from your own domain. The browser then sees a "1st-party" call, no different from loading an image or a stylesheet. Now no blocker blocks it.

This page shows you how, with first-party setups that fit a European infrastructure. Choose the host and execution region to match your compliance, performance, and support needs.

WordPress — go through the official plugin

On WordPress, don't dive into Nginx, Caddy or DNS. The official Snorklee Analytics plugin 2.3.4+ already does all the work:

  1. Install or update the plugin from the Snorklee dashboard or the Snorklee WordPress plugin page.
  2. In wp-admin, open Snorklee.
  3. Verify the site domain and keep the dashboard URL set to https://snorklee.com, unless you run a self-hosted instance.
  4. Enable Self-host mode and save.
  5. Run Test installation. The result should mention self-host or 1st-party proxy detected.

The plugin serves /js/flow.js from WordPress, forwards /api/event and /api/ping, keeps the JavaScript cached for 1 hour (via the Transients API), and stores no events in WordPress.


How it works — high-level diagram

┌──────────────────────────────────────────────────────────────────────────┐
│                                                                          │
│   BEFORE (3rd-party, blocked)                                            │
│   ───────────────────────                                                │
│                                                                          │
│   browser ──► <script src="https://snorklee.com/w.js">                │
│               ❌ ERR_BLOCKED_BY_CLIENT                                    │
│                                                                          │
│   browser ──► POST https://snorklee.com/api/event                     │
│               ❌ ERR_BLOCKED_BY_CLIENT                                    │
│                                                                          │
└──────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│                                                                          │
│   AFTER (1st-party via your proxy, never blocked)                        │
│   ───────────────────────────────────────────                            │
│                                                                          │
│   browser ──► <script src="/js/flow.js"> on yoursite.com                 │
│               ✅ served by your proxy from snorklee.com               │
│                                                                          │
│   browser ──► POST yoursite.com/api/event                                │
│               ✅ relayed by your proxy to snorklee.com                │
│                                                                          │
└──────────────────────────────────────────────────────────────────────────┘

You relay the script and 2 API endpoints from your domain to snorklee.com:

Path on your domainTarget on snorkleeRole
/js/flow.jssnorklee.com/w.jsTracker script
/api/eventsnorklee.com/api/eventPageviews + custom events
/api/pingsnorklee.com/api/ping"Live" heartbeat every 30s

The code you put in <head> becomes:

<script defer src="/js/flow.js" data-site="yoursite.com"></script>

Every path is 1st-party: from the browser's point of view, nothing leaves for an outside domain. It's your proxy that quietly bridges to snorklee.com, server-side, with the visitor none the wiser.

What relaying costs — the visitor's address: when your server relays /api/event, it is your server we see, never the browser. The address you forward in X-Forwarded-For does reach us, but we refuse to turn it into a location: on a public endpoint, anyone could declare any address they like. On a relayed install there is therefore no country and no region, and unique visitors become unreliable (they are derived from that same address, so distinct visitors merge into one). That is not the only measurement affected: the visit key is derived from that same address, so traffic source attribution, the direct/AI split, live visitors and engagement also become unreliable, and the anti-abuse cap (50 requests per minute per address) now applies to your whole site at once. Page views and robot visits are still counted. Snorklee tells you so in the decision queue instead of drawing a false map. Keep X-Forwarded-For in the recipes below anyway: it is what lets us verify the identity of AI robots. If the map matters more to you than blocker bypass, relay only the script (see the FAQ at the bottom of this page).

Recipe 1 — Nginx (most universal)

Works on: any Linux VPS (OVH, Scaleway, Hetzner, Clever Cloud with Nginx runtime, IONOS, Infomaniak…).

Sovereignty: neutral (depends on your hosting provider — choose EU).

Add this block inside the server { ... } that serves your site (typically /etc/nginx/sites-available/yoursite.conf):

# snorklee — 1st-party proxy (anti-blocking)
location = /js/flow.js {
    proxy_pass        https://snorklee.com/w.js;
    proxy_set_header  Host snorklee.com;
    proxy_ssl_server_name on;
    proxy_set_header  X-Forwarded-For $remote_addr;
    proxy_set_header  X-Real-IP       $remote_addr;
    proxy_set_header  User-Agent      $http_user_agent;
    proxy_set_header  Accept-Language $http_accept_language;
    proxy_hide_header Set-Cookie;
    proxy_read_timeout 10s;
}

location ~ ^/api/(event|ping)$ {
    proxy_pass        https://snorklee.com$request_uri;
    proxy_set_header  Host snorklee.com;
    proxy_ssl_server_name on;
    proxy_set_header  X-Forwarded-For $remote_addr;
    proxy_set_header  X-Real-IP       $remote_addr;
    proxy_set_header  User-Agent      $http_user_agent;
    proxy_set_header  Accept-Language $http_accept_language;
    proxy_set_header  Origin          $http_origin;
    proxy_hide_header Set-Cookie;
    proxy_read_timeout 10s;
}

Then:

sudo nginx -t && sudo systemctl reload nginx

To check: open https://yoursite.com/js/flow.js in your browser — you should see the minified Snorklee code. If you get a 502, check that proxy_ssl_server_name on; is set (required for SNI to snorklee.com).


Recipe 2 — Caddy (simplest)

Works on: any host running Caddy (auto-TLS included).

Sovereignty: neutral.

In your Caddyfile, inside your site block:

yoursite.com {
    # ... your existing config ...

    # snorklee — 1st-party proxy (anti-blocking)
    @snorkleeApi path /api/event /api/ping

    handle_path /js/flow.js {
        rewrite * /w.js
        reverse_proxy https://snorklee.com {
            header_up Host snorklee.com
            header_down -Set-Cookie
        }
    }

    handle @snorkleeApi {
        reverse_proxy https://snorklee.com {
            header_up Host snorklee.com
            header_down -Set-Cookie
        }
    }
}

Then:

caddy reload --config /etc/caddy/Caddyfile
Caddy 2.6+ syntax: use a named matcher (@snorkleeApi path …) to apply handle to multiple paths. The form handle /a /b /c { … } is not accepted by the parser and fails caddy validate.
Caddy injects X-Forwarded-For and X-Real-IP automatically.

Recipe 3 — Apache (mod_proxy)

Works on: shared hosting (OVH, Infomaniak, IONOS) and any Apache server with mod_proxy enabled. Useful for WordPress on cPanel without root access.

Sovereignty: neutral.

In your .htaccess (site root) or <VirtualHost>:

# snorklee — 1st-party proxy (anti-blocking)
SSLProxyEngine On

# Tracker script
RewriteEngine On
RewriteRule ^js/flow\.js$ https://snorklee.com/w.js [P,L]

# API
RewriteRule ^api/(event|ping)$ https://snorklee.com/api/$1 [P,L]

# Preserve visitor IP (setifempty avoids overwriting an upstream XFF
# if you sit behind another LB/reverse-proxy that already sets one)
ProxyPreserveHost Off
RequestHeader setifempty X-Forwarded-For "%{REMOTE_ADDR}s"
RequestHeader setifempty X-Real-IP       "%{REMOTE_ADDR}s"

# Never forward visitor-side session cookies
Header always unset Set-Cookie

Required modules (typically already enabled on serious EU shared hosts): mod_proxy, mod_proxy_http, mod_ssl, mod_rewrite, mod_headers. On OVH shared, ask support to enable if missing — it's free and standard.


Recipe 4 — Bunny.net Edge Scripting

For whom: high-traffic sites that want a CDN edge closer to the visitor (latency win + origin offload).

European footprint: Bunny.net is operated by BunnyWay d.o.o. in Slovenia. If you want routing to stay close to Europe, limit the pricing zones to Europe during setup.

Pricing: ~€0.01 per million Edge Script requests + ~€0.005 per GB CDN bandwidth. For 1M pageviews/month, count ~€5 total. No minimum, pay-as-you-go.

Steps:

  1. Create an account on bunny.net (credit card, ~€5 free initial credit).
  2. Create a "Pull Zone":
  • CDN tab → Add Pull Zone
  • Name: mysite-snorklee (free choice)
  • Origin URL: https://snorklee.com
  • Pricing tier: Standard
  • Pricing zones: you can keep only Europe if you want to limit routing and costs to that zone
  1. Connect your domain via CNAME:
  • Hostnames tab → Add Hostnameflow.yoursite.com
  • At your DNS registrar (Gandi, OVH, etc.): add CNAME flow.yoursite.com → mysite-snorklee.b-cdn.net
  • Wait 5 min, return to Bunny → click Generate Free SSL Certificate (auto Let's Encrypt)
  1. Map the path:
  • Edge Rules tab → Add Edge Rule
  • Action: Override URL
  • Match: Request URL contains "/js/flow.js"
  • Override URL: https://snorklee.com/w.js
  1. Final snippet:
<script defer src="https://flow.yoursite.com/js/flow.js" data-site="yoursite.com"></script>

All /api/* paths automatically pass through to snorklee.com/api/* via the Pull Zone.

Note: Bunny forwards X-Forwarded-For by default, which is what lets us verify AI robot identity. The countries map stays empty on any relayed install — see the callout at the top of this page.

Recipe 5 — Next.js / Nuxt rewrites

For whom: modern JavaScript-stack sites whose host supports rewrites or server routes.

Hosting: check that your platform preserves the useful headers (X-Forwarded-For, HTTP method, User-Agent, Accept-Language) and that the chosen execution region matches your privacy and performance needs.

Possible options include classic Node/SSR hosting, serverless with an explicit region, a Docker container, or a frontend platform that exposes server-side rewrites.

Next.js — in next.config.js:

module.exports = {
  async rewrites() {
    return [
      { source: '/js/flow.js', destination: 'https://snorklee.com/w.js' },
      { source: '/api/event', destination: 'https://snorklee.com/api/event' },
      { source: '/api/ping', destination: 'https://snorklee.com/api/ping' },
    ];
  },
};

Nuxt 3 — in nuxt.config.ts:

export default defineNuxtConfig({
  routeRules: {
    '/js/flow.js': { proxy: 'https://snorklee.com/w.js' },
    '/api/event': { proxy: 'https://snorklee.com/api/event' },
    '/api/ping': { proxy: 'https://snorklee.com/api/ping' },
  },
});
Next/Nuxt rewrites preserve X-Forwarded-For and HTTP method automatically. POST events go through correctly.

Choosing your host

The first-party proxy works with plenty of hosts. Before you pick one, check above all:

  • the execution region actually used by the proxy;
  • the host-side log retention period;
  • correct forwarding of X-Forwarded-For (it verifies AI robot identity; it does not locate your visitors);
  • the ability to remove unnecessary cookies or headers;
  • the contractual commitments you need for your own privacy policy.

Verify it works

  1. The script loads — open https://yoursite.com/js/flow.js in your browser: you should see the minified Snorklee code (it starts with !function()...).
  2. Events leave — open DevTools (F12) → Network tab, then click around your site. You should see POST /api/event return 204 No Content.
  3. What you will not see — on a relayed install the Countries card stays empty and unique visitors are approximate. That is expected, not a misconfiguration (see the callout at the top of this page), and no proxy setting changes it today.
  4. The result in the Integration tab — the "Test installation" probe spots self-host mode on its own and shows "1st-party proxy detected" in the result.

FAQ

Does the proxy slow down my site? No, or barely. The flow.js code is ≈ 2 KB gzip and stays cached in the browser. Events leave via sendBeacon, which blocks nothing — so the visitor notices no difference, even if the proxy adds 50 ms.

What if my proxy goes down? The events for that stretch are lost (no offline queue — a deliberate choice, §25 TDDDG / GDPR minimization). Your site itself keeps running normally — only audience measurement pauses. Same as any other third-party service that goes down.

Can I relay only the script, not the API? Yes, with the data-api attribute:

<script
  defer
  src="/js/flow.js"
  data-site="yoursite.com"
  data-api="https://snorklee.com"
></script>

The script is served 1st-party (it slips past blockers that filter on the filename), but events go straight to snorklee.com, where blockers that filter on the domain re-block them. It's a trade-off, not a half measure: you lose the blocked share of visits, and in exchange you keep your visitors' country and reliable unique visitor counts — which a full relay cannot give back.

What if I switch analytics tool later? The proxy structure doesn't change — you only swap the target domains. The <script src="/js/flow.js" data-site="..."> code stays the same everywhere.


Need help?

  • 🛠️ Dashboard Integration tab → Test installation button (auto HTTP probe)
  • 📧 DPO / support contact: see Compliance tab in the dashboard
  • 📚 Full docs: /docs