DNS over TLS: Setup Guides for Routers, Servers, and Clients

Encrypted DNS changes how your network handles name resolution by moving queries from UDP/53 to TLS on TCP/853 with certificate validation and persistent connections.
We’ll show reliable ways to enable DNS over TLS (DoT) on routers, server-side resolvers, and end-user clients, plus commands to verify strict TLS, detect leaks, and avoid common breakage.
The flow is straightforward: your stub or caching resolver validates a server certificate for a published authentication name (for example one.one.one.one, dns.google, or dns.quad9.net) and then transports DNS messages inside that encrypted session; strict profiles refuse cleartext or invalid certificates so you don’t silently downgrade.
Because DoT changes transport semantics, you’ll want to tune caches, MTU-sensitive options, and firewall rules so reliability and latency stay predictable under load.
Why DNS Over TLS Matters
Plain DNS is readable and malleable in transit; DoT hides queries from passive observers and blocks simple on-path tampering, while DNSSEC (if enabled) validates answer integrity at the data layer; middleboxes that relied on plaintext inspection won’t see your queries anymore.
Prerequisites and Core Concepts
Pick resolvers that publish a TLS authentication domain and certificate policy (one.one.one.one for 1.1.1.1, dns.google for 8.8.8.8, dns.quad9.net for 9.9.9.9); correct time is mandatory because TLS rejects skewed clocks; prefer strict DoT with verification tied to an auth name over opportunistic modes that might fall back to UDP/53 without notice.
Router Setup (OpenWrt-Style, Embedded Linux)
Most routers ship with dnsmasq for DHCP and DNS; to add DoT, run a local caching forwarder that speaks plain DNS to LAN clients but forwards upstream via TLS; Unbound or Stubby are proven options on OpenWrt-like systems.
Option A: Unbound as a DoT Forwarder
Install Unbound and a CA bundle, enable the service, then define a forward-zone with TLS and pinned authentication names; Unbound listens on 53 for the LAN and connects out on 853.
# Router shell
opkg update
opkg install unbound ca-bundle ca-certificates
# /etc/unbound/unbound.conf.d/dot-forward.conf
server:
cache-min-ttl: 120
cache-max-ttl: 86400
edns-buffer-size: 1232
prefetch: yes
qname-minimisation: yes
tls-cert-bundle: "/etc/ssl/certs/ca-certificates.crt"
forward-zone:
name: "."
forward-tls-upstream: yes
forward-addr: 1.1.1.1@853#one.one.one.one
forward-addr: 9.9.9.9@853#dns.quad9.net
# enable and start
/etc/init.d/unbound enable
/etc/init.d/unbound restart
Point dnsmasq (or your DHCP server) at 127.0.0.1 for DNS, or advertise the router’s LAN IP; remove any WAN DNS overrides so only Unbound goes upstream.
Option B: Stubby (getdns) as a DoT Stub
Stubby is a small DoT stub that listens locally and forwards to providers with strict verification; you can pair it with dnsmasq for caching or let Unbound cache while Stubby handles upstream TLS.
opkg update
opkg install stubby ca-bundle ca-certificates
# /etc/stubby/stubby.yml (strict profile)
resolution_type: GETDNS_RESOLUTION_STUB
dns_transport_list:
- GETDNS_TRANSPORT_TLS
tls_authentication: GETDNS_AUTHENTICATION_REQUIRED
round_robin_upstreams: 1
idle_timeout: 6000
listen_addresses:
- 127.0.0.1@5453
- ::1@5453
upstream_recursive_servers:
- address_data: 1.1.1.1
tls_port: 853
tls_auth_name: "one.one.one.one"
- address_data: 9.9.9.9
tls_port: 853
tls_auth_name: "dns.quad9.net"
/etc/init.d/stubby enable
/etc/init.d/stubby restart
Configure dnsmasq to forward to 127.0.0.1#5453 (and ::1#5453 for IPv6) and confirm that WAN egress is TCP/853 only.
Server Setup (Expose Your Own DoT Resolver)
Hosting a resolver with inbound DoT gives you fleet-wide policy and a single encrypted endpoint; you’ll need a hostname with a valid certificate, firewall rules for TCP/853, and a resolver that accepts TLS.
Unbound: Inbound DoT
Enable a TLS listener on 853 with your key and certificate, bind on public addresses, and keep any legacy plain-DNS on a separate interface if you still serve internal clients; enable DNSSEC validation to catch tampering upstream.
# /etc/unbound/unbound.conf.d/dot-inbound.conf
server:
interface: 0.0.0.0@853
interface: ::0@853
tls-service-key: "/etc/letsencrypt/live/dns.example.com/privkey.pem"
tls-service-pem: "/etc/letsencrypt/live/dns.example.com/fullchain.pem"
hide-identity: yes
hide-version: yes
rrset-roundrobin: yes
qname-minimisation: yes
harden-dnssec-stripped: yes
If you front the resolver with a TCP/TLS stream proxy, preserve client addresses only if both sides support PROXY protocol v2; otherwise terminate TLS on Unbound to keep logs and rate limits accurate.
Knot Resolver: Inbound and Outbound DoT
Knot Resolver can accept client DoT and forward upstream via DoT with hostname authentication; enable TLS listeners, load certificates, then define a TLS forward policy for upstreams.
-- /etc/knot-resolver/kresd.conf
net.listen('0.0.0.0', 853, { kind = 'tls' })
net.listen('::', 853, { kind = 'tls' })
net.tls('/etc/knot-resolver/server-cert.pem', '/etc/knot-resolver/server-key.pem')
cache.size = 100 * MB
modules.load('policy')
policy.add(policy.all(policy.TLS_FORWARD({
{'1.1.1.1', hostname='one.one.one.one'},
{'9.9.9.9', hostname='dns.quad9.net'}
})))
Recent BIND 9.18+ can also serve DoT via tls and listen-on tls blocks, and modern PowerDNS Recursor supports outbound DoT; verify your exact version and feature flags before production.
Client Setup
Clients can speak DoT directly to a public resolver or to your router/server; always prefer strict validation so you don’t silently fall back to UDP/53.
Android (Private DNS)
Android 9+ supports system-wide DoT; go to Settings → Network & Internet → Private DNS, choose “Private DNS provider hostname,” and enter one.one.one.one, dns.google, or dns.quad9.net; IPs alone won’t work because Android validates the certificate against the hostname.
Linux with systemd-resolved
systemd-resolved enforces strict DoT to named servers using the “IP#hostname” form; point resolv.conf at the stub and enable TLS.
# /etc/systemd/resolved.conf.d/dot.conf
[Resolve]
DNS=1.1.1.1#one.one.one.one 9.9.9.9#dns.quad9.net
DNSOverTLS=yes
# activate
sudo mkdir -p /etc/systemd/resolved.conf.d
sudo systemctl restart systemd-resolved
sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
Use resolvectl status to confirm “DNS Over TLS: yes (strict)” and that servers show the expected hostnames.
iOS and macOS
Create and install a DNS Settings configuration profile (via MDM or manually) that specifies DNS over TLS and the resolver hostname; this applies system-wide on Wi-Fi and cellular; third-party stubs remain optional.
Windows Note
Windows 11 includes native DNS-over-HTTPS for the system resolver; system-wide DoT isn’t generally available in stable releases, so if you need DoT specifically, run a local stub (Stubby or Unbound) on 127.0.0.1 and point Windows at it over classic DNS while the stub upgrades upstream to DoT.
Testing and Proving It Works
To test a provider directly with strict TLS and hostname validation, use kdig which speaks DoT natively and verifies against your CA store; to inspect only TLS, you can use openssl with SNI set.
# strict DoT with hostname validation
kdig @1.1.1.1 +tls-ca +tls-hostname=one.one.one.one example.com A
# TLS handshake and certificate peek
openssl s_client -connect 1.1.1.1:853 -servername one.one.one.one -quiet
To verify your router or stub is the only upstream path, capture traffic while generating lookups; you should see TCP/853 to your chosen providers and no UDP/53 to the internet.
# on the router or host
tcpdump -ni any 'port 53 or port 853'
# generate some load through your local resolver
for i in {1..5}; do dig +short example${i}.com @127.0.0.1; done
# on systemd-resolved hosts
resolvectl status | grep -i "DNS Over TLS"
Avoiding Leaks and Breakage
Keep time correct (enable NTP) so TLS validation succeeds; pin hostnames, not just IPs, because SNI and certificate checks use names; prefer strict modes over opportunistic to avoid silent downgrade; set EDNS buffer near 1232 bytes to reduce fragmentation; use long-lived connections and prefetch to cut handshake overhead; for captive portals, DoT will fail until you complete sign-on, so allow clear DNS only on a quarantined onboarding network.
Operational Tips
Configure at least two upstream DoT providers and enable health checks; export metrics for query rates, TLS failures, and fallbacks; record provider hostnames in configuration management so audits can confirm that strict validation is enabled end to end.