Tech Expert & Vibe Coder

With 15+ years of experience, I specialize in self-hosting, AI automation, and Vibe Coding – building applications using AI-powered tools like Google Antigravity, Dyad, and Cline. From homelabs to enterprise solutions.

Creating n8n Workflows to Track DNS Censorship Changes Across ISPs

Why I Built DNS Censorship Tracking Workflows

I run my own DNS infrastructure across multiple ISPs in India. Over the past year, I noticed something frustrating: websites would become unreachable without warning, not because they were down, but because my ISP had quietly blocked them at the DNS level. Sometimes it was a legitimate court order. Other times, it seemed arbitrary.

What bothered me wasn’t just the blocking—it was the lack of transparency. I had no way to know when a domain was censored, which ISP was doing it, or whether the block was temporary or permanent. I needed a system that would alert me when DNS responses changed across different providers, so I could understand what was actually being filtered.

I already had n8n running for other automation tasks, so I decided to use it to track DNS censorship across my three ISPs: Airtel, Jio, and my local fiber provider.

My Real Setup

I run n8n inside a Docker container on Proxmox, alongside my Synology NAS and Pi-hole instances. The workflow I built queries a list of domains against multiple DNS servers every 6 hours and compares the responses. If a domain that previously resolved suddenly returns NXDOMAIN or gets redirected to a block page IP, I get a notification.

Here’s what I actually used:

  • n8n workflow – The core automation engine
  • DNS queries via HTTP Request nodes – Using Google’s DNS-over-HTTPS API and Cloudflare’s DoH endpoint
  • Comparison logic in Function nodes – JavaScript to detect when responses change
  • Persistent storage in a local SQLite database – To track historical DNS responses
  • Telegram notifications – Alerts sent to my phone when blocks are detected

I started with a small list of 20 domains—news sites, VPN providers, and a few services I use regularly. The workflow runs every 6 hours and stores the results locally. If a domain’s IP changes or disappears, the workflow flags it and sends me a message.

How the Workflow Actually Works

The workflow has four main stages:

1. Query DNS Servers

I use n8n’s HTTP Request node to query DNS-over-HTTPS endpoints. I chose DoH instead of traditional DNS queries because it’s harder to intercept and gives me more reliable results. Each domain in my list gets queried against three resolvers:

  • Google Public DNS (8.8.8.8 via DoH)
  • Cloudflare DNS (1.1.1.1 via DoH)
  • My ISP’s default resolver (via direct UDP query using a custom script)

I intentionally included my ISP’s resolver because that’s where the censorship actually happens. Comparing it against Google and Cloudflare shows me whether the block is local or global.

2. Parse and Normalize Responses

DNS responses come back as JSON from the DoH endpoints. I use a Function node to extract the IP addresses and status codes. If a domain returns NXDOMAIN, I log it as “blocked.” If it returns an IP that belongs to a known block page (I maintain a small list of these), I flag it as “redirected.”

For my ISP’s resolver, I run a small Python script that does a direct DNS lookup and returns the result as JSON. n8n calls this script via an HTTP Request to a local Flask endpoint I set up.

3. Compare Against Historical Data

This is where the workflow gets useful. Every time a domain is queried, the result is stored in a local SQLite database with a timestamp. The Function node compares the current response to the last recorded response. If they don’t match, it triggers an alert.

I wrote the comparison logic to ignore minor changes like TTL updates or load-balanced IPs. It only flags real changes—when a domain that previously resolved now returns NXDOMAIN, or when the IP suddenly points to a block page.

4. Send Notifications

When a change is detected, the workflow sends a Telegram message with:

  • The domain that changed
  • The old IP vs. the new IP (or “NXDOMAIN”)
  • Which DNS server returned the changed response
  • A timestamp

I chose Telegram because it’s reliable, doesn’t require a phone number to be exposed, and I already use it for other server alerts.

What Worked

The workflow has been running for about four months now. It caught several real censorship events:

  • News site blocks – A regional news site suddenly returned NXDOMAIN on my ISP but resolved fine on Google DNS. Turned out to be a temporary court order.
  • VPN provider blocks – One VPN’s website started redirecting to a block page IP on Jio, but not on Airtel or my fiber connection.
  • Intermittent blocks – Some domains would be blocked for a few hours, then unblocked. Without the workflow, I would have assumed the site was just down.

The most useful part was the historical comparison. I could see exactly when a block started and whether it was consistent across ISPs. That helped me decide whether to switch ISPs for certain services or just use a VPN.

The workflow also helped me identify false positives. A few times, a domain’s IP changed because of legitimate CDN updates, not censorship. The historical data made it easy to distinguish between the two.

What Didn’t Work

The biggest issue was false positives from CDN changes. Some domains use Cloudflare or Akamai, and their IPs rotate frequently. My initial logic flagged every IP change as suspicious, which flooded me with useless alerts.

I fixed this by adding a whitelist of known CDN IP ranges. If a domain’s new IP falls within those ranges, the workflow ignores the change. This reduced false positives by about 80%.

Another problem was rate limiting. Google’s DoH API has a rate limit, and I hit it a few times when testing with a larger domain list. I had to add a delay between queries (2 seconds per domain) to avoid getting temporarily blocked.

The third issue was storage growth. The SQLite database grew faster than I expected because I was storing every single DNS response. After a month, it was over 500 MB. I added a cleanup job that deletes records older than 90 days, which keeps the database under 100 MB.

Finally, my ISP’s resolver sometimes times out. When that happens, the workflow logs it as “no response” instead of “blocked,” which is technically correct but not very useful. I haven’t found a clean way to distinguish between a timeout and an intentional block yet.

Key Takeaways

This workflow gave me visibility into something that was previously invisible. I now know when domains are being censored, which ISP is doing it, and whether it’s consistent across providers. That’s valuable information for deciding when to use a VPN or switch ISPs.

A few practical lessons:

  • Use DoH for reliability – DNS-over-HTTPS is harder to intercept and gives cleaner results than traditional DNS queries.
  • Store historical data – The real value comes from comparing current responses to past responses. Without that, you’re just doing one-time checks.
  • Expect false positives – CDNs and load balancers will trigger alerts unless you explicitly handle them.
  • Keep the domain list small – I started with 20 domains. More than that, and you’ll hit rate limits or generate too much noise.
  • Run queries at intervals, not continuously – Every 6 hours is enough to catch most blocks without overloading DNS servers.

The workflow isn’t perfect. It doesn’t catch every type of censorship (like deep packet inspection or SNI filtering), and it can’t tell me why a domain was blocked. But it does what I needed: it alerts me when DNS responses change, so I can investigate further.

If you’re running your own DNS infrastructure or just want to know when your ISP is blocking domains, this is a practical way to track it. The workflow is simple enough to set up in an afternoon, and it runs reliably with minimal maintenance.

Leave a Comment

Your email address will not be published. Required fields are marked *