DEV Community

Cover image for CNAME analytics proxies are dead, and custom reverse proxies are borrowed time
Nikita Savchenko
Nikita Savchenko

Posted on • Originally published at dataunlocker.com

CNAME analytics proxies are dead, and custom reverse proxies are borrowed time

I originally published this on the DataUnlocker blog, where I built a web analytics proxy and have watched ad-blocker filter lists closely for years. This version focuses on the engineering decision: which "beat ad blockers" proxy is worth building on, and which is already dead.

Open the docs of almost any analytics product and you'll find a page on proxying it past ad blockers. The pitch never changes: route the tracking through your own domain so it looks first-party, and the blocker can't tell it apart from your real code.

The oldest version of this is also the one still offered almost everywhere: a "managed" proxy with a CNAME DNS record. Point a subdomain at your vendor, swap one line of script, done. No server to run. It also stopped working in March 2021, when AdGuard started to run their DNS "uncloaking" service.

A proxy almost never fails on day one. You wire it up, your numbers tick up a few percent, you think it's solved. The failure comes weeks or months later, quietly, after you've forgotten it exists, when your proxy's address lands in a public filter list and the recovered data just stops. No alert. You find out when you notice declines in your dashboards.

CNAME and a custom reverse proxy are both on that clock. They just run down at very different speeds.

CNAME proxies: blocked within days

A CNAME proxy points a subdomain like some-prefix.yoursite.com at your analytics vendor. The request leaves your domain, so a naive blocker that only knows the vendor's domain misses it. That worked for a couple of years, until the blocker ecosystem went after the DNS layer directly.

AdGuard runs the resolver side of this. Their users sit behind AdGuard DNS, so when a custom subdomain resolves to a known analytics endpoint, AdGuard sees it and adds it to a public, automatically-updated list of CNAME-cloaked trackers: thousands of entries, the largest list of its kind (The Record covered it at ~6K entries). Those updates get downstreamed into other filter lists and reach end users within about a week. Palo Alto's Unit 42 documented the same DNS technique from the tracking side.

On Firefox, uBlock Origin uncloaks CNAMEs on the fly: it resolves each request's hostname through the browser DNS API, and if that subdomain's CNAME lands on a domain already in its lists, it blocks the request before the script loads, so nobody has to catalogue your specific subdomain first. It's on by default on Firefox, and the code was reworked in 2024 to filter on the resolved IP too. Brave does the same in its engine. Chrome extensions are the odd exception: they get no DNS API, so they can't.

The strongest evidence isn't mine. It's the vendors saying so in their own docs. One open-source analytics vendor's own proxy discussion admits ad blockers now crawl the web for CNAMEs and block them once discovered, so the CNAME gives you no better stats than their default script. Another vendor went further and pulled the feature entirely: their own explanation was that they could spend hundreds of development hours adjusting how to bypass blockers, only to be undone by a single line from a filter-list maintainer. A third keeps the feature but is blunt that it's "only intended as a vanity domain" and won't stop blockers.

Custom reverse proxies: blocked within months

The stronger version is a true reverse proxy: you serve analytics from your own origin under your own path (yoursite.com/abc/collect) with no CNAME to anything. There's no DNS chain to follow and the request is genuinely same-origin, so domain-based filtering and CNAME uncloaking both miss it. This works today, and it's what most vendors now recommend over CNAME.

The catch is that "the URL is on your domain" isn't the same as "the URL is invisible". Filter lists match more than domains. They also match paths and query parameters regardless of domain (see uBlock's static filter syntax). If your proxy keeps the recognizable shape (/g/collect, /gtag/js, ?v=2&tid=G-…), a generic rule still catches it on your first-party domain.

It happened in a 2020 proof-of-concept. Someone proxied gtag.js through their own domain. uBlock Origin still blocked it, not because it figured out the proxy, but because EasyList carries a generic /gtag/js path rule that fires on any domain. AdGuard, with a different list, let it through, which tells you how arbitrary the line is.

So a reverse proxy survives only if you also rename the path and every part of it that can match a filter, and keep renaming whenever a generic rule generalizes or someone reports your domain by hand. Custom reverse proxies buy you months instead of days, but it's the same machine.

Can a blocker detect the analytics product behind a renamed proxy?

Say your path and parameters are renamed and nothing in the URL gives the vendor away. Can a blocker still tell which analytics tool is running, from the shape of the data it sends or what the script does once it runs?

As of mid-2026, no shipping blocker does this for a genuinely first-party, renamed proxy. uBlock Origin's static filters match URLs, domains, and paths, not request-body shape. Its scriptlets can neutralize analytics by known global name (window.gtag, window.ga), but not if you avoid those names. The content-and-behavior approaches (AST classifiers like ASTrack, Brave's research on JS-behavior signatures) exist in papers and roadmaps, not in the blocker you have installed. Manifest V3 actually pushes the other way, limiting what an extension can inspect about a request.

So today the honest answer is: a well-hidden custom proxy is caught mainly by URL patterns you forgot to rename, or by someone reporting your domain by hand, not by payload or behavior. The research is moving that way, though, and the people building it have more time and tooling than any single site owner.

What actually lasts

Step back and the pattern is clear: every proxy moves the tracker somewhere new and waits to be found by the blocker community again. The work is yours, the deadline is theirs, and the only variable is how long until the next rule.

The one approach that doesn't decay does the opposite: stop hiding the data, and make blocking it cost the user. Tie the analytics into the app itself, so the same request that carries your events also carries something the page needs to work. Now a blocker can't strip the tracking without breaking the site, and breaking sites is the one thing blockers work hard to avoid. That's the idea behind DataUnlocker, and it's deliberately analytics-agnostic: it wires up whatever you already use rather than replacing it.

It's not free of trade-offs: cosmetic and script filters can still partially break a page, and there's a real cost to coupling data to functionality. I wrote the practical version, including a single-endpoint trick you can do without any product, in a companion piece: how to keep analytics past ad blockers, and which methods actually last. If you assumed server-side GTM already solved this, it's worth a read too. And if you just want to know whether a proxy you already run has been found, you can monitor the filter lists for your domain for free.

Building a reverse proxy for analytics isn't wrong. It's just borrowed time, and it's worth knowing the loan's terms before you build on it.


Originally published on the DataUnlocker blog.

Top comments (0)