Skip to content

A DNS Block That Looks Like a TLS Error (HTTPS-First vs Pi-hole)

🛑 Problem

A site that unquestionably supports HTTPS — gemini.google.com, say — refuses to load, and the browser shows:

This site doesn't support a secure connection Attackers can see and change information you send or receive from the site.

The message points squarely at TLS. There is a "Continue to site" button, implying an HTTP fallback exists. Neither is true: the site's TLS is fine, and continuing will not help. The domain is being blocked at DNS, and the browser is misreporting why.

This is the modern counterpart to the older ERR_CONNECTION_REFUSED symptom (see Pi-hole AI Blocklist Blocks Claude Desktop) — same root cause, different-looking error, because browsers changed.


🔁 Why This Happens

Pi-hole in NULL blocking mode answers blocked domains with 0.0.0.0.

Browsers now default to HTTPS-First (Chrome, Edge, Helium, and Chromium forks generally). The sequence:

  1. Browser resolves gemini.google.com → 0.0.0.0 from the blocking resolver.
  2. HTTPS-First insists on https:// and opens a TCP connection to 0.0.0.0.
  3. That connection cannot complete a TLS handshake — there is nothing there.
  4. The browser cannot distinguish "no TLS at this address" from "this server doesn't do HTTPS," so it emits its generic insecure-connection interstitial.

The DNS block is never surfaced. The browser has no idea the resolver lied to it — 0.0.0.0 is a perfectly well-formed DNS answer.

Important

Treat "doesn't support a secure connection" on a site that obviously supports HTTPS as a DNS block until proven otherwise. Reach for dig before you touch certificates, clocks, or TLS settings. Chasing this as a TLS problem is a dead end — there is no certificate to inspect, because there is no server.


🔍 Diagnosis

The tell is a disagreement between your resolver and a public one. One command:

dig +short example-blocked-domain.com              # your resolver
dig +short @1.1.1.1 example-blocked-domain.com     # public resolver
Result Meaning
Local → 0.0.0.0, public → real IPs DNS block. Continue below.
Both → real IPs Not a DNS block; it may genuinely be a TLS problem.
Both → NXDOMAIN The domain really is gone.

Confirm which resolver you're actually using — a VPN or tailnet may be supplying one you didn't expect:

scutil --dns | grep 'nameserver\[0\]'   # macOS
resolvectl status                        # systemd-based Linux

Ask Pi-hole what blocked it

The single most useful command, run on the Pi-hole host:

pihole -q example-blocked-domain.com

It names the offending adlist directly:

Found 1 lists exactly matching 'example-blocked-domain.com'.

  - https://raw.githubusercontent.com/NotaInutilis/Super-SEO-Spam-Suppressor/main/hosts.txt (block)

✅ Fix

Add an exact allowlist entry (domainlist type 0), which overrides any gravity/adlist block:

sudo pihole allow example-blocked-domain.com

Verify it landed, and that the domain now resolves:

sudo sqlite3 /etc/pihole/gravity.db \
  "SELECT id, type, enabled, domain FROM domainlist WHERE domain = 'example-blocked-domain.com';"
# → 427|0|1|example-blocked-domain.com     (type 0 = exact allow, enabled)

dig +short @<pihole-ip> example-blocked-domain.com    # → real IPs, not 0.0.0.0

Then hard-reload the page (Ctrl/Cmd + Shift + R). The browser caches the failed HTTPS attempt and will keep showing the interstitial against a now-working domain otherwise.

Note

Prefer allowlisting the specific domain over dropping the adlist. A list can be simultaneously wrong about one domain and valuable for the other several hundred thousand.


🎯 Why Broad Adlists Cause This

The lists most likely to catch a legitimate service are the largest ones, and AI/SEO-oriented lists are the usual suspects — they enumerate AI endpoints and scraper hostnames by domain, sweeping up the consumer-facing product alongside them.

List Scale Notes
HaGeZi TIF ~1.6M Most aggressive; first suspect for any unexpected block
Super-SEO-Spam-Suppressor ~815k Mostly typosquat/phishing, but blocks gemini.google.com and ai.google
OISD ~485k Broad
uBlockOrigin HUGE AI ~2.2k Targets AI endpoints by design; blocks claude.ai

If you run several of these, expect a steady trickle of false positives on AI services specifically. That is a reason to keep an allowlist, not necessarily to drop the lists.


🔧 Make It Survive a Rebuild

An allowlist entry added with pihole allow lives only in gravity.db. Rebuild the host, or restore a Teleporter backup, and it is gone — and the failure returns wearing the same misleading TLS costume.

Declare allowlisted domains in configuration management instead. An Ansible sketch:

# group_vars/pihole/vars.yml
pihole_allowlist:
  - gemini.google.com
# reconcile gravity.db against the declared list
- name: Read existing exact allowlist entries
  ansible.builtin.command:
    cmd: sqlite3 /etc/pihole/gravity.db "SELECT domain FROM domainlist WHERE type=0;"
  register: existing_allow
  changed_when: false
  check_mode: false          # must run under --check so the task below evaluates honestly

- name: Add missing allowlist domains
  ansible.builtin.command:
    cmd: "pihole allow {{ item }}"
  loop: "{{ pihole_allowlist }}"
  when: item not in (existing_allow.stdout_lines | default([]))
  changed_when: true
  notify: reload pihole lists

Warning

command tasks are skipped in check mode by default. Without check_mode: false on the read task, a --check run reports a pending change on a host that is already correct — and, worse, tells you nothing about whether the reconcile logic works. Verify idempotency with a real run, not a dry one.

Run the allowlist step on its own with --tags allowlist.


🔎 Quick Reference

# 1. Is it DNS? (resolver disagreement = yes)
dig +short blocked.example.com
dig +short @1.1.1.1 blocked.example.com

# 2. What blocked it?
ssh <pihole-host> 'pihole -q blocked.example.com'

# 3. Allow it
ssh <pihole-host> 'sudo pihole allow blocked.example.com'

# 4. Confirm
dig +short @<pihole-ip> blocked.example.com

# 5. Hard-reload the browser (Cmd/Ctrl + Shift + R)

See Also