Suppose a laptop opens a domain you meant to block, while Pi-hole’s query log shows nothing. The tempting fix is another blocklist; the less satisfying discovery is that the browser never asked Pi-hole in the first place. It may have sent the lookup to another resolver over DNS-over-HTTPS (DoH), inside an ordinary HTTPS connection. I’d follow that missing query before touching the lists. Otherwise, it is remarkably easy to build an impressive set of rules around traffic that is going somewhere else.
Know which DNS path you are controlling
Plain DNS usually uses UDP or TCP port 53. DoH carries DNS over HTTPS, normally alongside other web traffic on port 443. DNS-over-TLS (DoT) normally uses TCP port 853. These are different transports, so a port-53 redirect does not turn a TLS handshake into a plain DNS query. The DoH standard[1] and DoT standard[2] describe the protocols.
Start on that laptop with a fresh lookup and Pi-hole’s log open. First check that Pi-hole’s privacy setting exposes the domains and clients needed for the test; hidden log details are not evidence of a bypass. Check the DNS servers supplied by DHCP and IPv6 router advertisements, then compare a dig or nslookup result with what the browser does. The two can take different paths. If neither query appears, investigate the configured resolver, caching and any VPN; if the command reaches Pi-hole but the browser does not, inspect its secure-DNS setting. An empty log narrows the investigation, but it does not name the culprit for you.
Put the ordinary route through Pi-hole
Advertise Pi-hole's address as the DNS server for each intended client network. Check IPv4 DHCP and, if you use IPv6, the DNS information clients receive through IPv6. Give Pi-hole a stable address and confirm it can reach its chosen upstream resolver. Pi-hole's prerequisites[3] distinguish IPv4 and IPv6 firewall rules; an IPv4-only policy cannot claim to cover dual-stack clients.
Once the ordinary lookup works, choose how to handle a client that insists on another plain-DNS server. Blocking client UDP and TCP port 53 to outside resolvers is the simpler policy; leave the intended clients access to Pi-hole on that port. A destination NAT rule can instead redirect those port-53 packets to Pi-hole, but test the return path as carefully as the destination. When client and Pi-hole share a subnet, the reply can bypass the router unless the design also handles source NAT. OPNsense’s hairpin NAT guide[4] explains that same-subnet case. If the redirect’s packet path is unclear, I’d keep the explicit block and configure the client rather than hide the problem behind another rule.
Whichever approach you choose, exclude Pi-hole’s own upstream requests. Router syntax and ordering differ: OPNsense documents that NAT runs before filter rules[5], followed by floating, group and interface rule processing. Read that order before deciding which exception the new block will respect.
The next detour is direct DNS-over-TLS. Block client TCP port 853 when you do not authorize it, then check how the client reacts. Redirecting that connection to plain port 53 gives Pi-hole a TLS handshake where it expects DNS. It does not convert one protocol into the other. A client configured for encrypted DNS only may stop resolving rather than fall back, which is an unpleasant surprise to discover on someone else’s laptop.
Keep the transport names on the rule record. DNS-over-QUIC[6] uses UDP port 853 by default, and HTTP/3[7] carries HTTP over QUIC. A TCP-only block or connection test does not cover UDP. Decide how those paths fit your network policy instead of counting one failed DoT connection as proof that all encrypted DNS is stopped.
Keep the rule scope explicit. A rule that blocks a whole LAN including Pi-hole can cut off Pi-hole's own upstream path; an exception placed after a first-match block may never run. Put a narrow Pi-hole exception ahead of the client block or match only client source addresses. If Pi-hole itself uses an encrypted upstream, allow that approved destination and transport. OPNsense's rule guide[5] explains first-match and stateful behavior. The same IoT rule-order discipline applies here: read the effective floating, group and interface rules, not only the tab where you just added a block.
Before applying a redirect or block, decide what happens when Pi-hole is unavailable. A hard block of external port 53 means clients cannot simply use a public plain-DNS resolver when the local server fails. A redirect depends on both the router and Pi-hole staying up. If you add a second local resolver, configure and test it as part of the policy rather than slipping an unfiltered public server into DHCP as a supposed backup. Pi-hole's filtering and allowlist controls are useful only on queries that reach a filtering resolver.
Keep a short rule record for each client segment:
| Traffic from clients | Intended action | Verify with |
|---|---|---|
| DNS to Pi-hole on UDP/TCP 53 | Pass | Fresh query visible in Pi-hole |
| DNS to other addresses on UDP/TCP 53 | Block or explicit port-53 redirect | Query to a chosen outside resolver fails or appears at Pi-hole |
| DoT to outside TCP 853 | Block unless approved | Connection test and firewall log |
| Known DoH endpoints on a chosen HTTPS transport | Optional, limited block | Request to that specific endpoint fails |
| General HTTPS traffic | Continue normal web policy | Ordinary sites still work; DoH remains a possible gap |
Repeat the policy for IPv6 and every VLAN or guest interface it is meant to cover. A rule on the main LAN does not cover a device’s cellular connection or reveal DNS inside an encrypted VPN that terminates elsewhere. When a new block appears ineffective, inspect the firewall’s existing states as well; a previously permitted flow may continue under its old state and obscure the result of the change.
Control DoH where you control the client
For a browser you administer, I’d put the rule in the browser as well. A policy directly controls its DoH mode; an address list only covers the endpoints it happens to include. Firefox's DNSOverHTTPS policy[8] can set Enabled: false and lock the choice. Chromium's DnsOverHttpsMode policy[9] has an off mode. Microsoft Edge's policy[10] also defines off. Apply a policy only to devices you administer, then verify it in that browser's policy/settings page and Pi-hole's log. A browser policy does not govern every other application on the device.
Firefox also has a network signal: use-application-dns.net. Mozilla says[11] a negative DNS answer can disable Firefox's default-enabled DoH. It does not override a user's explicit choice to enable DoH. Current Pi-hole configuration[12] already has dns.specialDomains.mozillaCanary enabled by default, returning NXDOMAIN for the canary's A and AAAA queries. Check that setting before adding a redundant blocklist entry, and test a Firefox profile's actual mode rather than assuming every Firefox installation obeys the signal.
For unmanaged phones, TVs or guest laptops, you may not be able to set application policy. Separate them into a guest network if they need a different trust level. Blocking known resolver addresses or DoH bootstrap hostnames is an optional partial control: it can interrupt clients that use those endpoints, but providers change addresses and a client can choose another HTTPS endpoint or connect by IP. A Pi-hole domain rule only sees a bootstrap lookup that actually reaches Pi-hole. It cannot inspect arbitrary encrypted HTTPS payloads, and blocking a shared IP can also break unrelated services. Review what a domain rule really matches before using a broad wildcard; the Pi-hole allowlist and regex guide explains that the chosen pattern can affect more services than intended.
Test the promise, including failures
Use one client and one rule at a time; capture the before and after result:
| Test | Expected observation | What it does not prove |
|---|---|---|
| Fresh lookup using the advertised resolver | A matching query appears in Pi-hole's log | Every application uses that resolver |
| Query an external resolver on UDP/TCP 53 | The router blocks or redirects it as configured | DoH and DoT are blocked |
| Attempt TCP 853 to an outside DoT resolver | The connection fails under a block rule | The client falls back to Pi-hole |
| Browser with managed DoH policy off | The browser reports the policy and new lookups appear in Pi-hole | Other apps cannot use DoH |
| Firefox with default DoH and a negative canary answer | Firefox uses the system resolver in that mode | User-selected DoH is overridden |
To test a known DoH path, ask a browser or tool to query a resolver that you have intentionally blocked, then check its behavior and the firewall log. If an HTTPS DoH request succeeds, the IP or domain block did not cover that path. If it fails and the firewall records the intended block, you have shown only that specific path was blocked. Cloudflare's 1.1.1.1/help check[13] reports whether that client is using Cloudflare; it cannot certify every resolver or every app on the network.
Test again from an IPv6-capable client and after relevant browser or device updates. Read firewall counters beside Pi-hole’s log so you can distinguish an intercepted 53 or 853 attempt from the DoH traffic HTTPS still conceals. If an encrypted-DNS-only client loses connectivity, decide whether to manage its configuration or document an exception before calling the policy finished.
A maintainable boundary
What I would avoid is the reassuring shortcut: redirect port 53, see a blocked test query, and declare every device covered. Keep the smaller, useful promise instead. Ordinary DNS goes through the local filter; managed browsers have a checked policy; endpoint blocks cover named destinations on named transports. When the hypothetical laptop gets through again, that record tells you where to look—and which claim you never made.