diff --git a/src/analytics.js b/src/analytics.js index f4da148..6663c9d 100644 --- a/src/analytics.js +++ b/src/analytics.js @@ -27,6 +27,9 @@ export function sanitizePosthogEvent(event) { '$lib','$lib_version','$browser','$browser_version','$os','$os_version','$device_type', '$screen_height','$screen_width','$viewport_height','$viewport_width','$timezone', '$host','$pathname','$title','$is_identified','$process_person_profile','page_path','page_title', + // How PostHog recognises that ingestion is proxied. It holds our own + // public origin, and without it the reverse proxy check never passes. + '$lib_custom_api_host', 'concept','placement','destination',...campaignKeys, ...webVitalsMetrics.map(name => `$web_vitals_${name}_value`)]); const properties = Object.fromEntries(Object.entries(event.properties || {}).filter(([key,value]) => keep.has(key) && ['string','number','boolean'].includes(typeof value))); diff --git a/test/analytics.test.mjs b/test/analytics.test.mjs index 49dbd0d..192e368 100644 --- a/test/analytics.test.mjs +++ b/test/analytics.test.mjs @@ -77,6 +77,8 @@ test('PostHog loads and ingests through the same origin so blockers cannot drop const options = h.win.posthog._i[0][1]; assert.equal(options.api_host,'https://www.javascriptin30words.com/e30'); assert.equal(options.ui_host,'https://eu.posthog.com','Links into PostHog still point at the real app'); + assert.ok(sanitizePosthogEvent({event:'$pageview',properties:{$lib_custom_api_host:'https://www.javascriptin30words.com/e30'}}).properties.$lib_custom_api_host, + 'PostHog detects the proxy from this property, so it has to survive sanitizing'); const loader = h.scripts.map(element=>element.src).find(src=>src.includes('/e30/')); assert.equal(loader,'https://www.javascriptin30words.com/e30/static/array.js'); assert.ok(!h.scripts.some(element=>element.src.includes('posthog.com')),'No request reveals the vendor hostname');