Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/app/(rewrites)/[[...slug]]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export async function GET(request: NextRequest): Promise<Response> {
allowIndexing: ALLOW_SEO_INDEXING,
},
hrefPrefixes: [rewrittenPrefix, 'https://e2b.dev'],
stripAnalytics: process.env.VERCEL_ENV !== 'production',
})
}

Expand Down
39 changes: 39 additions & 0 deletions src/lib/utils/rewrites.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 <script>/<noscript>/<iframe> elements and drops any whose src or
* inline content matches a known analytics vendor.
*
* @param $ The Cheerio API instance.
*/
function stripAnalyticsTags($: cheerio.CheerioAPI): void {
$('script, noscript, iframe').each((_, element) => {
const $element = $(element)
const haystack = `${$element.attr('src') ?? ''} ${$element.html() ?? ''}`

if (ANALYTICS_TAG_MATCHERS.some((matcher) => haystack.includes(matcher))) {
$element.remove()
}
})
}

/**
* Rewrites HTML content for content pages by applying multiple transformations.
* This includes SEO tag rewrites and absolute href rewrites.
Expand All @@ -138,6 +172,7 @@ function rewriteContentPagesHtml(
options: {
seo: SeoTagOptions
hrefPrefixes?: string[]
stripAnalytics?: boolean
}
): string {
const $ = cheerio.load(html)
Expand All @@ -148,6 +183,10 @@ function rewriteContentPagesHtml(
rewriteAbsoluteHrefsInDoc($, options.hrefPrefixes)
}

if (options.stripAnalytics) {
stripAnalyticsTags($)
}

return $.html()
}

Expand Down
62 changes: 62 additions & 0 deletions tests/unit/rewrites-strip-analytics.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { describe, expect, it } from 'vitest'
import { rewriteContentPagesHtml } from '@/lib/utils/rewrites'

const FIXTURE_HTML = `<!doctype html>
<html>
<head>
<title>Landing</title>
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});})(window,document,'script','dataLayer','GTM-K2W3LVD2');</script>
<script>!function(t,e){window.posthog=e;e.init('phc_x',{api_host:'https://us.posthog.com'})}(document,window.posthog||[]);</script>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-ABC123"></script>
<script>gtag('js', new Date()); gtag('config', 'G-ABC123');</script>
<script src="https://snap.licdn.com/li.lms-analytics/insight.min.js"></script>
<script src="https://cdn.prod.website-files.com/site/js/app.js"></script>
<script>console.log('page bootstrap, not analytics');</script>
</head>
<body>
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-K2W3LVD2"></iframe></noscript>
<a href="https://e2b.dev/pricing">Pricing</a>
</body>
</html>`

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')
})
})
Loading