Webclat / PostHog Practice

Setting up a PostHog reverse proxy so ad blockers stop eating your data

PostHog's default ingestion domain is a third-party request from the browser's point of view - which means content blockers, ITP, and some corporate network filters drop it before it ever reaches PostHog. A reverse proxy makes ingestion first-party. It is a routing decision, not a PostHog setting.

What a reverse proxy is actually doing here

Without a proxy, the browser sends capture requests straight to PostHog's own domain (Cloud or self-hosted equivalent). Every popular ad blocker and privacy browser ships a filter list that recognizes analytics-vendor domains and drops the request client-side - before it ever leaves the page. A reverse proxy rewrites that request so it appears to come from a path on your domain (for example yourapp.com/ingest), which most blocklists do not target because they cannot distinguish it from any other first-party API call.

This is purely a network-routing change. It does not alter what PostHog captures, how events are processed, or your event schema - it changes which domain the browser talks to.

When you actually need this

  • Measurable capture loss. If PostHog's own comparison of ingested events versus server-side or backend-confirmed events shows a gap, ad-blocker loss is the first thing to rule out - this requires a real before/after measurement on your traffic, not an assumed percentage; published ad-blocker adoption rates vary by audience and should not be treated as your number.
  • Safari and ITP-affected traffic. Safari's Intelligent Tracking Prevention treats some third-party analytics cookies more aggressively than first-party ones regardless of ad blockers.
  • Corporate or education network filtering. Some managed networks block known analytics domains at the DNS or proxy layer, independent of the browser.

Implementation done properly

Pick the path that matches how your app is already deployed - do not add a new piece of infrastructure just to run a proxy if your framework or edge platform already has a documented pattern for it.

Deployment shapeProxy approachWhat to verify before shipping
PostHog Cloud + Vercel/Cloudflare/NetlifyManaged reverse proxy through the platform's own edge configConfirm the platform's managed-proxy feature is still current - this is a vendor feature that changes; check current docs for exact setup steps and plan availability
Next.jsRoute handler or middleware rewrite to the ingestion pathMiddleware must not run on every route if it adds latency to unrelated requests
Nuxt / SvelteKitServer route or hook rewriting the capture endpointConfirm the rewrite preserves query params PostHog needs (project key, compression flag)
Self-managed edge (nginx, Caddy, Cloudflare, Kubernetes ingress)A dedicated location/route block proxying to PostHog's ingestion hostTLS termination and header forwarding (especially client IP) configured correctly
PostHog self-hostedRunning PostHog itself behind your own reverse proxy for the app UI, separate from ingestion proxyingDistinguish "proxying ingestion for ad-blocker evasion" from "proxying the self-hosted app for TLS/access" - different problems, same word

Version and platform specifics move: PostHog has shipped and later deprecated specific managed-proxy integrations before. Verify the exact setup steps against current PostHog docs rather than a screenshot from an older guide - this page describes the routing concept and the decision framework, not a copy-pasteable config.

How to verify it worked

  1. Open browser devtools network tab on a page with a common ad blocker enabled and confirm the capture request now shows your domain, not PostHog's.
  2. Confirm the request still returns a success status and the event shows up in PostHog's live events view within the normal ingestion delay.
  3. Re-run whatever gap measurement flagged the problem originally (backend event count vs. PostHog-captured count) and confirm the delta actually closed - a proxy that is misconfigured can silently fail closed, which looks identical to "no problem" until you check the count.
  4. Check for CORS or mixed-content errors in the console - a proxy that changes domains can break assumptions elsewhere in the page if it is not scoped narrowly to the ingestion path.

Proxy work often surfaces alongside a self-hosted deployment decision, since both involve owning more of the network path than PostHog Cloud's default.

Frequently Asked Questions

Does a reverse proxy change what PostHog captures?

No. It changes which domain the browser sends the request to. The event payload, schema, and processing are unaffected - this is a network routing decision, not a tracking-configuration one.

Will a proxy fix all ad-blocker loss?

It fixes loss caused by domain-based blocklists, which is the majority case. It does not help with blockers that use browser-extension heuristics unrelated to domain, or with users who have JavaScript disabled entirely. Measure before and after on your own traffic rather than assuming full recovery.

Do you set this up as part of an implementation engagement?

Yes - proxy setup is typically bundled with an event-architecture or migration engagement rather than sold standalone, since it needs to be verified against your actual capture-loss numbers, not installed on faith.

Get your ingestion path audited, not guessed at.

We measure the actual capture gap first, then scope the proxy approach that fits your existing deployment - no infrastructure added that the numbers do not justify.

Audit My Capture Loss