
A popup opened anyway.
My home network was already sending DNS through NextDNS. I had reputable ad and tracker lists enabled. Plenty of junk was being blocked. Yet a couple of sites could still throw a new tab at me as if none of that protection existed.
I went into the logs expecting to find an obvious failure. Instead, I found something more useful: several suspicious requests had been blocked exactly as expected. The filter was working. It just had not blocked every request that mattered.
That distinction changed the whole investigation.
The wrong first move is adding more lists
When an ad slips through, the tempting response is to enable every blocklist with an intimidating name. I understand the urge. If one net has a hole, throw five more nets on top of it.
But more lists do not tell me which request escaped. They add overlap, make future breakage harder to explain, and can increase false positives. In my case, the existing lists were already catching most of the random-looking domains on the page.
The better question was not, “Why did DNS filtering fail?” It was, “Which hostname caused the bad behaviour, and what did my DNS filter do with that hostname?”
The browser creates suspects; DNS logs establish the verdict
I reproduced the problem in a controlled browser session and collected the hostnames requested by the page. A quick version of that technique in the browser console looks like this:
[...new Set(
performance.getEntriesByType("resource")
.map(entry => new URL(entry.name).hostname)
)].sort()
That list is not proof of guilt. A page can legitimately load fonts, images, CDNs, analytics, video hosts, and half the modern internet. It is only a suspect list.
The DNS logs supplied the other half. I matched those hostnames against queries from the same time window and checked whether each request was allowed or blocked, and which rule made the decision.
The pattern became obvious. Several word-salad, throwaway-looking domains were already blocked. One more stable infrastructure hostname was allowed. A fresh random-looking domain also got through at first, then appeared as blocked by a list shortly afterward. That looked like a list-freshness gap: the domain existed and was useful to the popup network before the shared list caught up.
The popup stack did not need every domain to work. It needed one. A dashboard can truthfully report thousands of blocked queries while the browser still gets hijacked by the single request that escaped.
Static lists are memory, not prediction
A traditional blocklist is mostly a collection of names already identified as unwanted. It can be enormous and well maintained, but a brand-new domain has to be discovered, classified, added, distributed, and loaded before the list can block it.
That is why rotating infrastructure is effective. Some networks generate or cycle through disposable domains faster than static lists can follow. I am using “DGA-style” descriptively here; a strange hostname alone does not prove malware. It does mean that chasing each leaf one by one is a losing game.
NextDNS’s current public API reflects the layered approach: it exposes threat feeds and DGA detection, privacy blocklists, a manual denylist, and query logs as separate controls. Automated detection helps, but it does not make every new advertising hostname predictable.
DNS filtering also has a natural boundary. It decides whether a hostname resolves. It cannot see which URL path, button, or script is misbehaving once traffic to an allowed hostname is flowing. That is why DNS filtering and a browser content blocker complement each other rather than replacing each other.
Block the trunk, not just the leaves
Once I had the browser and DNS evidence side by side, I stopped focusing only on the disposable names. I looked for the more stable infrastructure underneath them: a recurring host or parent domain shared by the popup chain.
I added the confirmed throwaway domains and the narrowest useful infrastructure domain to my denylist, then repeated the test. The unwanted path stopped resolving.
Blocking a parent domain is not automatically safe. Shared hosting can put unrelated services under the same roof. Before doing it, I check recent DNS history, look for legitimate use elsewhere on my network, and keep the change easy to reverse. Precision beats anger-driven wildcard blocking.
The workflow I will reuse
This is now my repeatable process when a popup survives DNS filtering:
- Reproduce it in a controlled browser session.
- Capture the page’s resource hostnames and the approximate time.
- Match those candidates against DNS logs.
- Separate already-blocked noise from the requests that were actually allowed.
- Look for stable infrastructure beneath rotating domains.
- Denylist the narrowest confirmed target, retest, and watch for collateral damage.
- If the network rotates again, repeat the evidence collection instead of guessing.
This method also prevents a common mistake: blaming the filtering service when the logs show that it did exactly what its rules told it to do. The missing ingredient may simply be a name no rule knew yet.
A bigger list is still a tradeoff
I also considered moving to a stronger list. HaGeZi’s current lineup, for example, offers several tiers on NextDNS, from lighter options through Pro, Pro++ and Ultimate. The project labels those tiers from relaxed or balanced to increasingly aggressive, which is the honest way to describe the choice.
A broader list may close more gaps, but it may also break more legitimate things. I did not enable a more aggressive tier automatically. I fixed the confirmed infrastructure first and kept the broader change as a deliberate next step if the problem returns often enough.
The biggest lesson was not about one popup network. It was about observability. “My blocker is on” is not a diagnosis. Browser resources show what a page tried to load; DNS logs show what the resolver allowed; repeated testing shows whether the fix worked.
Next time something slips through, I will not start by asking which giant list I can add. I will ask a narrower question: which request got through, and what stable piece of infrastructure is hiding underneath it?



