From 58b512d5f4d5685a4705d1bf37169b81adf43723 Mon Sep 17 00:00:00 2001 From: Alex Drankou Date: Thu, 9 Jul 2026 10:36:36 +0200 Subject: [PATCH] feat: strip analytics from webflow wepbage during rewrite --- src/app/(rewrites)/[[...slug]]/route.ts | 1 + src/lib/utils/rewrites.ts | 39 +++++++++++++ tests/unit/rewrites-strip-analytics.test.ts | 62 +++++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 tests/unit/rewrites-strip-analytics.test.ts diff --git a/src/app/(rewrites)/[[...slug]]/route.ts b/src/app/(rewrites)/[[...slug]]/route.ts index d6f2f3f9c..6882a62dd 100644 --- a/src/app/(rewrites)/[[...slug]]/route.ts +++ b/src/app/(rewrites)/[[...slug]]/route.ts @@ -100,6 +100,7 @@ export async function GET(request: NextRequest): Promise { allowIndexing: ALLOW_SEO_INDEXING, }, hrefPrefixes: [rewrittenPrefix, 'https://e2b.dev'], + stripAnalytics: process.env.VERCEL_ENV !== 'production', }) } diff --git a/src/lib/utils/rewrites.ts b/src/lib/utils/rewrites.ts index a9a01eb3e..27988d056 100644 --- a/src/lib/utils/rewrites.ts +++ b/src/lib/utils/rewrites.ts @@ -125,6 +125,40 @@ function rewriteAbsoluteHrefsInDoc( }) } +// Substrings identifying analytics tags baked into the proxied Webflow HTML. +// GTM is the single loader — GA4 and the LinkedIn Insight Tag are injected by it +// at runtime, so removing the GTM loader stops all three. PostHog and the +// remaining GA/LinkedIn entries are matched directly as defense-in-depth. +const ANALYTICS_TAG_MATCHERS = [ + 'googletagmanager.com', + 'gtm.start', + 'GTM-', + 'google-analytics.com', + 'gtag(', + 'snap.licdn.com', + 'px.ads.linkedin.com', + '_linkedin_partner_id', + 'posthog', +] + +/** + * Removes third-party analytics tags from an HTML document using Cheerio. + * Scans only + + + + + + + + + + Pricing + +` + +const seo = { pathname: '/', allowIndexing: false } + +describe('rewriteContentPagesHtml — stripAnalytics', () => { + it('removes every analytics vendor when stripAnalytics is true', () => { + const out = rewriteContentPagesHtml(FIXTURE_HTML, { + seo, + stripAnalytics: true, + }) + + expect(out).not.toContain('gtm.start') + expect(out).not.toContain('GTM-K2W3LVD2') + expect(out).not.toContain('googletagmanager.com') + expect(out).not.toContain('posthog') + expect(out).not.toContain('gtag(') + expect(out).not.toContain('snap.licdn.com') + }) + + it('keeps non-analytics scripts and page markup when stripping', () => { + const out = rewriteContentPagesHtml(FIXTURE_HTML, { + seo, + hrefPrefixes: ['https://e2b.dev'], + stripAnalytics: true, + }) + + expect(out).toContain('cdn.prod.website-files.com/site/js/app.js') + expect(out).toContain('page bootstrap, not analytics') + expect(out).toContain('href="/pricing"') + }) + + it('leaves analytics tags intact when stripAnalytics is falsy (production)', () => { + const out = rewriteContentPagesHtml(FIXTURE_HTML, { + seo, + stripAnalytics: false, + }) + + expect(out).toContain('GTM-K2W3LVD2') + expect(out).toContain('posthog') + expect(out).toContain('snap.licdn.com') + expect(out).toContain('www.googletagmanager.com/ns.html') + }) +})