Repository navigation
Ad blockers: fetch transport, settings proxy recipes and guide - #149
Conversation
Blockers classify beacons as "ping" requests, which filter lists block wholesale. fetch with keepalive also survives page exit, and a string body keeps it a simple request without a preflight.
Settings -> Installation explains what the tracker already does and offers nginx, Caddy, Cloudflare Worker and Next.js proxy configurations filled in with the site's ID and host, the first-party snippet, and server-side events. On managed instances it links the site's proxy key and sends the key headers; self-hosted, it explains SUPERMETRY_TRUSTED_PROXIES.
Post the verification event with the site's public ID (not its slug) and match recipe tabs exactly now that a "Next.js proxy" tab exists.
Findings
|
| Layer / File(s) | Summary |
|---|---|
Fetch keepalive transport internal/tracker/tracker.js, internal/tracker/tracker_runtime_test.js, internal/tracker/tracker_test.go, web/src/lib/tracker-full.test.ts, CHANGELOG.md |
The tracker sends POST requests with keepalive: true and catches fetch failures. Tests check the fetch configuration and reject sendBeacon use. |
First-party proxy setup
| Layer / File(s) | Summary |
|---|---|
Proxy recipe generation web/src/features/settings/installRecipes.ts, web/src/features/settings/installRecipes.test.ts |
Adds a first-party script path and proxy recipes for nginx, Caddy, Cloudflare Workers, and Next.js. Managed recipes include proxy-key handling; tests check generated paths, headers, and syntax. |
Settings integration and setup guidance web/src/features/settings/InstallRecipes.tsx, web/src/features/settings/InstallRecipes.test.tsx, web/src/features/settings/SettingsPages.tsx, web/src/features/settings/SiteSettings.tsx, web/src/features/settings/site-settings.test.tsx, web/e2e/install-recipes.spec.ts, docs/guide/ad-blockers.md, docs/guide/configuration.md, README.md, CHANGELOG.md |
Settings uses the instance’s managed status to show proxy setup instructions and a generated first-party snippet. The guide, FAQ, and changelog describe proxy setup, server-side events, and related configuration. |
Possibly related PRs
- velixio/supermetry#116: Adds first-party proxy behaviour and defines proxy-key and client-IP header handling used by the new recipes.
- velixio/supermetry#113: Adds path-prefixed replay proxy support referenced by the new proxy recipes and guide.
- velixio/supermetry#137: Adds the managed-instance configuration flag used to select proxy-key instructions.
Merge Risk: 
Moderate · up to d7a95
-
Self-hosted users with a custom tracker path who follow the new first-party proxy instructions will get a script URL that does not resolve, so tracking stops. Events can also be lost when a cross-origin proxy redirects without CORS headers. Fix both before merging.
Security architecture risk: 
Moderate · up to d7a95
-
The optional proxy configurations introduce credential-handling risks: application Authorization headers can reach the analytics host, and the nginx example does not establish upstream certificate verification. Existing tracker privacy checks remain in place. Exposure depends on customers adopting these configurations and on their deployment controls.
Retained concerns
- Medium · security · inferred: The new nginx recipe forwards a managed proxy key and visitor traffic over HTTPS but enables only SNI, not upstream certificate verification. With nginx defaults and no inherited verification policy, an attacker able to intercept the upstream connection could impersonate the analytics host, capture credentials and telemetry, or replace tracker JavaScript executed on customer pages. The documentation repeats this configuration. This affects customers adopting the recipe, not every existing deployment.
- Medium · security · inferred: The new nginx, Caddy and Worker recipes remove cookies without excluding application Authorization headers. The Worker explicitly clones incoming headers; nginx and Caddy retain normal authorization forwarding behavior. On a customer origin using browser-managed HTTP authentication, same-origin tracker requests can therefore transfer application credentials to the analytics host. This newly recommended boundary differs from direct cross-origin tracking and from the SDK proxy, whose explicit header allowlist excludes Authorization. No affected production deployment was established.
Security review details
-
Security Blast Radius
- inferred — The independently exposed scope is a customer deployment adopting a recipe and the visitors whose traffic passes through it. Potentially exposed material includes that deployment's proxy key, visitor addresses, event or replay traffic, and application credentials carried in Authorization. Upstream script substitution could affect pages loading that proxy. Repository evidence does not establish cross-tenant privileges for a stolen proxy key.
Security Findings and Attack Paths
- inferred — The supported conditional attack paths are upstream impersonation when nginx certificate verification remains disabled, and application-credential disclosure when first-party tracking requests carry Authorization through a forwarding recipe. These arise from newly supplied deployment guidance; neither proves that a deployed customer is currently exploitable.
Trust Boundaries and Controls
- observed — The SDK-based path restricts methods and tracking paths, limits buffered uploads, copies only allowlisted headers, uses manual redirects and adds proxy credentials only to uploads with a resolved client address. It requires an explicit client-IP callback when a proxy key is configured. The new Next.js recipe satisfies that API requirement but delegates header authenticity to the deployment platform.
Resilience and Maintainability Implications
- observed — The shared SDK proxy returns 413 for oversized uploads and 502 for upstream fetch failures, with no-store responses for uploads. Its tests cover rejection of unrelated paths and methods and suppression of proxy credentials when the visitor address is missing. These controls do not establish equivalent behavior for every generated proxy platform.
Hardening Proposals
- proposed — Make authenticated upstream TLS and exclusion of application credentials explicit properties of the generated configurations. Separately, document supported client-IP trust sources and the managed key's site binding, fallback, rotation and revocation contract so deployment assumptions can be checked consistently.
Pre-merge checks: 
4 · 
1
-
Failed checks: 1 warning Passed checks: 4 passedFull details: Docstring Coverage
-
Explanation
Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 7 functions across 11 files. (4 skipped: 4 unsupported.)
Resolution
Write docstrings for the functions missing them to satisfy the coverage threshold.
-
CodeRabbit Verify
- Run CodeRabbit Verify to test the changes in this pull request.
Fix CodeRabbit findings from #149: no-cors events, proxy recipe paths
After the neutral
/e/<site>paths shipped, events were still blocked (net::ERR_BLOCKED_BY_CLIENT), while the script from the same host loaded fine. The tracker sent events withnavigator.sendBeacon, which blockers classify aspingrequests, and many filter lists block those outright.Tracker
fetch(..., { keepalive: true })instead ofsendBeacon. They still arrive on page exit, and the string body keeps it a simple request, so there is no preflight.sendBeaconis used (node runtime test and the web tracker tests).Settings → Installation → Ad blockers
This replaces the one-line hint with a full section:
/cloud/sites/<id>/proxy) and theX-Supermetry-Proxy-Key/X-Supermetry-Client-IPheaders.SUPERMETRY_TRUSTED_PROXIES./sm/js/<id>.js).Docs
docs/guide/ad-blockers.md: what is automatic, how to check (DevTools, uBlock logger), proxy steps and paths, an nginx example, and server-side events.Also fixed
e2e/install-recipes.spec.ts, which CI does not run, posted its verification event with the site slug instead of the public ID and always got a 400. Tab matching is now exact, because "Next.js proxy" also matched "Next.js".Verification
make web-test: typecheck and 203 files / 1,439 tests pass.go test ./internal/tracker/, nodetracker_runtime_test.jsandtracker_replay_runtime_test.jspass.make build:install-recipes.spec.tsandinstall-status.spec.tspass. I checked the rendered section in the full-page screenshot.