diff --git a/src/lib/telemetry.ts b/src/lib/telemetry.ts index c92e3fa..2dbe633 100644 --- a/src/lib/telemetry.ts +++ b/src/lib/telemetry.ts @@ -71,17 +71,17 @@ function send(envelopes: Envelope[]): void { if (!config || envelopes.length === 0) return; const url = `${config.ingestionEndpoint}/v2.1/track`; const payload = envelopes.map((e) => JSON.stringify(e)).join("\n"); - // sendBeacon is a background-priority transport, so the ingestion POST stays - // off the page's critical request chain. A plain fetch defaults to High - // priority, which Lighthouse counts as critical (this slow westus2 round-trip - // was dominating the chain). Beacon also reliably survives page unload, which - // is exactly what the visibilitychange/pagehide flush relies on. - if (typeof navigator.sendBeacon === "function") { - const blob = new Blob([payload], { type: "text/plain" }); - if (navigator.sendBeacon(url, blob)) return; - } - // Fallback for the rare browser without sendBeacon: an explicitly - // low-priority keepalive fetch so the request still stays off the chain. + // Deliberately NOT navigator.sendBeacon: beacon forces credentials mode + // "include" with no opt-out, so it replays the third-party cookies the Azure + // ingestion endpoint sets (ai_user, browserId) on every /v2.1/track POST. Our + // envelopes carry their own operationId/pageviewId, so we never need a cookie + // and shouldn't be leaking one to a third party. + // + // A keepalive fetch is the modern beacon equivalent and the only transport + // that lets us drop credentials: `credentials: "omit"` strips the cookies, + // `keepalive: true` survives page unload (what the visibilitychange/pagehide + // flush relies on), and `priority: "low"` keeps the slow westus2 round-trip + // off the page's critical request chain (the reason beacon was used before). fetch(url, { method: "POST", body: payload,