Module 4, Lesson 6: Bypass Ad Blockers with PostHog's Reverse Proxy
In this lesson, I’m picking up directly from where we left PostHog. Analytics is installed and working — but right now it only captures visitors who don’t have an ad blocker. This lesson fixes that.
Before We Start
Here’s what I’d expect you to have in place before this lesson:
From previous lessons:
- PostHog is installed and capturing events on your live landing page — you did this in Module 4, Lesson 5
- Your domain is on Cloudflare (or you can set it up there) — this is what enables the automatic proxy configuration
- The GitHub → Vercel deployment pipeline is working — push to GitHub, Vercel deploys (Module 4, Lesson 3)
- Claude Code is installed and running in your WebStorm terminal (Module 3, Lesson 3)
Tools / setup you’ll need:
- WebStorm (or VS Code) with Claude Code running in the terminal
- A PostHog account — you set this up in Module 4, Lesson 5
- Your domain’s DNS managed by Cloudflare — this is what makes the automatic proxy setup work
By the end of this lesson, you’ll:
- Understand why ad blockers silently break your analytics
- Have PostHog’s reverse proxy set up through your own domain
- Know how to update the PostHog API host in your codebase and deploy the change
- Be able to verify the proxy is actually working — with an ad blocker enabled
About This Lesson
Duration: ~7 minutes video + ~15 minutes practice Skill Level: Intermediate What You’ll Build: A reverse proxy configuration that routes PostHog tracking data through your own domain, making it invisible to ad blockers.
This is a short lesson but an important one. Most people skip it and then wonder why their analytics don’t add up. I want you to have this fixed before you start driving traffic — because once visitors start coming, you want to know you’re capturing all of them, not just the ones without ad blockers installed.
Watch the Lesson
<iframe src=“https://iframe.mediadelivery.net/embed/605189/c38df7c9–7267–4bed-a22a-f1301d70551f” allow=“autoplay; fullscreen; picture-in-picture” allowfullscreen loading=“lazy” style=“width:100%; aspect-ratio:16/9; border:none; border-radius:8px;”>
What We’re Covering
Here’s what I’m walking you through in this lesson and why it matters:
- The ad blocker problem — why a significant chunk of your visitors are invisible to PostHog right now
- How a reverse proxy fixes it — routing analytics traffic through your own domain so it doesn’t look like PostHog
- Setting up the proxy in PostHog — using PostHog’s managed proxy feature with Cloudflare
- Updating your codebase — one config change to tell PostHog to use your proxy endpoint
- Verifying it works — testing with an ad blocker actually enabled
1. The Problem: Ad Blockers Kill Your Analytics (~0:00)
At this point your PostHog setup is live. If you go into your project and hit Activities, then refresh your landing page, you should see events coming in — pageviews, clicks, whatever you’ve got set up.
But here’s the thing: that only works if the person visiting your page doesn’t have an ad blocker running. And a lot of people do. In tech audiences especially, it’s not unusual for 30–40% of visitors to be blocking trackers. On mobile — particularly on iPhone, where content blocking is on by default in many configurations — it’s even higher.
Those visitors don’t show up in your analytics at all. Not as bounces, not as low-engagement sessions — they’re just not there. Which means you’re making decisions based on a partial picture.
PostHog has a built-in solution for this: a reverse proxy. Instead of your page sending tracking data directly to app.posthog.com — which ad blockers recognise and block — you route it through a subdomain on your own domain. From the browser’s perspective, it looks like data going to your own site. Ad blockers can’t distinguish that from regular network traffic.
2. How the Reverse Proxy Works
2.1 The Core Idea
Right now, your PostHog script sends events to PostHog’s servers. The URL it hits — app.posthog.com — is on every ad blocker’s list. So the blocker intercepts it and drops the request.
With the reverse proxy, you set up a subdomain on your domain — something like g.yourdomain.com — that silently forwards all that traffic to PostHog on the backend. Your visitor’s browser sends data to g.yourdomain.com. Your server (Cloudflare, in this case) receives it and passes it along to PostHog. The visitor’s ad blocker sees a request to your own domain and lets it through.
The key thing PostHog tells you when setting this up: don’t use anything that makes it obvious it’s an analytics proxy. Don’t call the subdomain analytics.yourdomain.com or tracking.yourdomain.com. Use something generic — g, t, a, whatever. I used g.noturo.app in the lesson. It looks like any other request to my domain.
2.2 Why Cloudflare Makes This Easy
PostHog’s managed proxy feature integrates directly with Cloudflare. Instead of manually configuring DNS records and worker scripts yourself, PostHog can connect to your Cloudflare account and handle the configuration automatically. That’s what we’re using here.
If your domain isn’t on Cloudflare, you can still set up a reverse proxy — PostHog has documentation on doing it manually — but the automatic setup I’m showing only works when Cloudflare manages your DNS.
3. Set Up the Reverse Proxy in PostHog (~0:50)
3.1 Find the Proxy Settings
In your PostHog dashboard, look for the reverse proxy option. It’s typically under your project settings. You’re looking for something called Managed Proxy or Reverse Proxy — it may be labelled as beta, which is fine.
Click Add or Set Up Reverse Proxy.
3.2 Choose Your Subdomain
PostHog will ask you for a domain. This is the subdomain you want to use as your proxy endpoint.
Enter something generic. I used g.nurturo.app. The format is:
[something-generic].[your-domain].[tld]
Don’t use posthog, analytics, tracking, or anything that signals what it’s doing. Keep it short and ambiguous.
g, t, or a as the subdomain prefix is fine.3.3 Configure via Cloudflare
Once you’ve entered your subdomain, PostHog will offer to configure it automatically. Click Configure Automatically.
It’ll ask you to authorise your Cloudflare account. Go through the authorisation flow — log into Cloudflare if prompted, approve the connection, and let PostHog do its thing.
This sets up the necessary DNS records and Cloudflare Worker configuration automatically. You don’t need to touch Cloudflare yourself.
Once it completes, your proxy endpoint is live. But we’re not done — your app is still pointing at PostHog’s servers directly. We need to update that.
4. Update the PostHog API Host in Your Code (~2:55)
4.1 What Needs to Change
When your landing page initialises PostHog, it sends data to an API host. Right now that’s pointing to PostHog’s servers. We need to change it to point to your new proxy subdomain.
The config change is small — it’s a single URL — but it has to be in your code and deployed before the proxy does anything useful.
4.2 The Prompt
Open Claude Code in your WebStorm terminal. Use this prompt:
Update my PostHog API host to https://g.yourdomain.app
Replace g.yourdomain.app with whatever subdomain you set up in step 3.2. Claude will find the PostHog initialisation in your code and update the API host value.
If you’re comfortable finding it yourself, it’s usually in your main JS/TS file — look for the PostHog init() call. The option you’re changing is api_host. But letting Claude handle it is faster and you’re less likely to miss a second reference somewhere.
4.3 Commit and Deploy
Once Claude has made the change, tell it:
commit and push this
This commits the change and pushes to GitHub. Vercel picks it up automatically and deploys. Go to GitHub and look for the green checkmark next to the latest commit — that tells you the Vercel deployment succeeded.
5. Verify It’s Actually Working (~3:50)
This is the part I think is important to actually do — not just assume it’s working. Here’s how I tested it in the lesson.
5.1 Confirm Tracking Works Without an Ad Blocker First
Go to your PostHog dashboard and open the Live view under Activities. Then open your landing page in an incognito window and refresh a few times.
You should see events appearing in the Live view — something like “1 recently online” or “1 recently active recording.” That confirms PostHog is tracking without an ad blocker in play.
Close the incognito window.
5.2 Enable an Ad Blocker
Now enable an ad blocker. I use AdGuard — but whatever you have works. The point is to simulate what a real visitor with a blocker would look like.
With the blocker on, go back to PostHog Live view, then open your landing page and refresh.
Without the proxy, you’d see nothing. PostHog would show no activity from you. That’s the problem we just fixed.
5.3 Test With the Proxy Live
Make sure your Vercel deployment is complete (green checkmark on GitHub). Then repeat the test — ad blocker still on, refresh your landing page, watch the PostHog Live view.
You should now see events coming through, even with the blocker active. That’s the proxy working.
6. Try It Yourself
Exercise 1: Set Up the Proxy End-to-End
What to do: Follow sections 3, 4, and 5 above. Set up the managed proxy in PostHog, update the API host in your code via Claude Code, deploy, and verify with an ad blocker enabled.
A nudge if you’re stuck: The most common issue is the Cloudflare authorisation step. Make sure you’re logged into the right Cloudflare account — the one that manages the domain you’re using for your landing page. If PostHog can’t find your domain, check which account your DNS is under.
How you’ll know it’s working: PostHog Live view shows activity from your browser even when an ad blocker is enabled.
Exercise 2: Check Your Network Requests
What to do: With your landing page open in Chrome, open DevTools (F12 / Cmd+Option+I), go to the Network tab, and filter by your proxy subdomain (e.g. g.noturo.app). Refresh the page.
What you’re looking for: Requests going to your subdomain — not app.posthog.com. If you see them, the proxy redirect is happening client-side too.
What this is practising: Understanding what the proxy actually does at the network level. Useful for debugging if something stops working later.
7. You Should Be Able to Do This Now
Here’s what you can put together with what we just covered:
- Set up PostHog’s managed reverse proxy for any project using Cloudflare DNS
- Update a PostHog API host configuration using Claude Code
- Verify analytics integrity using PostHog’s Live view — with and without an ad blocker
Check Yourself
- I’ve set up the PostHog managed proxy with a generic subdomain name
- Cloudflare authorisation went through and the proxy configured automatically
- I’ve updated the
api_hostin my PostHog init to point to my proxy subdomain - I’ve committed and pushed the change — Vercel shows a green checkmark
- I’ve tested with an ad blocker enabled and can see events in PostHog Live view
If Something’s Not Working
What’s happening: PostHog is connecting to Cloudflare but can’t find the domain you entered. This usually means the domain is under a different Cloudflare account, or DNS isn’t managed by Cloudflare at all.
How to fix it: Log into Cloudflare and confirm your domain is listed there under the account you authorised. If your domain is at a different registrar with its own DNS (not using Cloudflare nameservers), you’ll need to either move DNS to Cloudflare or follow PostHog’s manual proxy setup docs.
What’s happening: Either the deployment hasn’t completed yet, or the
api_host in your code still points to PostHog’s servers.How to fix it: First, check GitHub — confirm the green checkmark is there. Then check your PostHog initialisation in your codebase and make sure
api_host is set to your proxy subdomain (e.g. https://g.yourdomain.app). If it still says https://app.posthog.com, the Claude Code update didn’t take or wasn’t committed.What’s happening: You may have used a name that’s on a common blocklist — like
analytics, tracking, or posthog as the subdomain prefix.How to fix it: Go back into PostHog’s proxy settings and change the subdomain to something completely generic —
g, t, x. Then update api_host in your code to match and redeploy.The Short Version
Here’s what I want you to walk away with:
- Ad blockers silently drop PostHog requests — without a proxy, you’re missing a significant slice of your traffic
- The fix is a reverse proxy — route analytics traffic through your own domain so it looks like a request to your own site
- PostHog’s managed proxy makes it easy — connect Cloudflare, pick a generic subdomain, done
- One code change to wire it up — update
api_hostin your PostHog init, commit, push, deploy - Always verify with a real ad blocker — don’t assume it’s working; test it
Quick Reference
The Proxy Subdomain Format
g.[yourdomain].[tld]
Generic. Nothing that signals analytics or tracking. Short prefix is fine.
The Code Change
In your PostHog init() call, update api_host:
posthog.init('phc_YOUR_KEY', {
api_host: 'https://g.yourdomain.app',
// ... other options
})
The Claude Code Prompt
Update my PostHog API host to https://g.yourdomain.app
Then:
commit and push this
How to Test
1. Open PostHog → Activities → Live view
2. Open your landing page in incognito → confirm events appear (no blocker)
3. Enable your ad blocker
4. Refresh your landing page
5. Check PostHog Live — you should still see events
Resources
Links & Docs
- PostHog Reverse Proxy Documentation — covers both managed (Cloudflare) and manual proxy setups
- PostHog Managed Reverse Proxy (Cloudflare) — specific to the Cloudflare automatic setup used in this lesson
Tools Used
- PostHog — product analytics, installed in Module 4, Lesson 5
- Cloudflare — DNS and proxy infrastructure
- Claude Code — AI coding assistant, running in the WebStorm terminal
- Vercel — deployment pipeline, connected since Module 4, Lesson 3
Questions I Get Asked
Q: Do I really need to do this? How many people actually use ad blockers?
More than you’d think — and it skews toward the exact demographic you’re probably targeting. Tech-savvy users, developers, and privacy-conscious people are much more likely to run ad blockers than the general public. If your product is aimed at that audience (and many of ours are), you could easily be missing 30–50% of visitors in your analytics without this fix.
Q: My domain isn’t on Cloudflare — can I still do this?
Yes, but you’ll need to do it manually. PostHog has documentation for setting up a reverse proxy with other providers like AWS CloudFront or NGINX. The automatic setup I showed only works with Cloudflare. If you want to keep things simple, the easiest path is to move your domain’s DNS to Cloudflare nameservers — it’s free and doesn’t change where your domain is registered, just where DNS is managed.
Q: What if the proxy subdomain itself gets added to a blocklist eventually?
It’s possible, but unlikely if you use a generic name. Ad blocker lists tend to target known tracking domains — not arbitrary subdomains on random people’s domains. And if it does happen, changing the subdomain and redeploying takes about 10 minutes.
Q: Does this have any performance impact on my landing page?
Negligible. The reverse proxy adds one hop in the network path for analytics requests, but those happen asynchronously in the background. They don’t block the page load or affect what your visitor sees.
Q: How do I know I’m ready for the next lesson?
Analytics works with ad blockers on. That’s the bar. When you can see events in PostHog Live view with AdGuard or uBlock or whatever you’re using enabled — you’re done here.
💬 Stuck? Come Talk to Us
The Product Path community → https://discord.gg/RFXRf9yg
Drop your question in the right channel. The community’s active and I check in there too.
Glossary
Reverse proxy: A server (or in this case, a Cloudflare Worker) that sits between your visitor’s browser and a third-party service. The browser sends requests to your domain; the proxy forwards them on to the real destination. From the browser’s perspective, it’s just talking to your site. (introduced in Module 4, Lesson 6)
Managed proxy (PostHog): PostHog’s built-in feature for automatically setting up a reverse proxy via Cloudflare. PostHog connects to your Cloudflare account and configures the DNS records and Worker routing on your behalf. (introduced in Module 4, Lesson 6)
Ad blocker: A browser extension or OS-level tool that blocks network requests to known tracking and advertising domains. Common examples include uBlock Origin, AdGuard, and Brave’s built-in shield. Blocks PostHog by default unless a reverse proxy is in place. (introduced in Module 4, Lesson 6)
API host (PostHog): The URL PostHog’s JavaScript SDK sends event data to. Defaults to https://app.posthog.com. Changed in this lesson to your proxy subdomain so requests are routed through your own domain instead. (first introduced in Module 4, Lesson 5 — Setup PostHog)
Cloudflare Worker: A serverless function that runs at Cloudflare’s edge — used here to receive analytics requests from your subdomain and forward them to PostHog. Set up automatically by PostHog’s managed proxy feature. (introduced in Module 4, Lesson 6)