DNS over TLS (DoT): Privacy-First Resolution

3 min read

## DoT vs. DoH: The Core Difference Both DNS-over-TLS (DoT) (DoT) and DNS-over-HTTPS (DoH) encrypt DNS queries to prevent eavesdropping. The difference is in how they are transported and on which port. DNS-over-TLS (DoT) wraps standard DNS wire-format messages in a TLS session on **port 853**. The DNS protocol itself is unchanged; TLS is a transparent security wrapper. Because DoT uses a dedicated port, network administrators can easily identify, monitor, or block encrypted DNS traffic — which some see as a feature (visibility) and others as a weakness (blockability). DNS-over-HTTPS (DoH) tunnels DNS inside HTTP/2 on port 443, where it is indistinguishable from ordinary web traffic. DoH is stealthier but also bypasses conventional network monitoring more aggressively, which is why enterprise environments often prefer DoT for when encrypted DNS is permitted at all. RFC 7858 standardizes DoT (2016). RFC 8310 adds a profile for authentication. Both predate the DoH RFC 8484 (2018), making DoT the older and in some respects more straightforward of the two approaches. ## How TLS Protects DNS When a DoT client connects to a resolver on port 853, a standard TLS handshake occurs. The resolver presents its certificate. The client validates the certificate against trusted CAs (or a pinned public key). Once the TLS session is established, DNS queries and responses flow as standard DNS wire format over the encrypted channel. TLS 1.3, now universally supported by DoT resolvers, reduces the handshake to a single round trip (1-RTT), and 0-RTT session resumption is possible for reconnecting clients, bringing latency close to unencrypted DNS for subsequent queries. ## Configuring DoT on Different Platforms ### Android (Private DNS) Android 9+ has native DoT support called "Private DNS": 1. Settings → Network & internet → Advanced → Private DNS 2. Select "Private DNS provider hostname" 3. Enter: `dns.google`, `one.one.one.one`, `dns.quad9.net`, or any DoT hostname 4. Android will automatically use port 853 with TLS validation Android applies Private DNS system-wide to all apps, including those that would otherwise use hardcoded resolvers. ### Linux (systemd-resolved) ```ini # /etc/systemd/resolved.conf [Resolve] DNS=9.9.9.9#dns.quad9.net 149.112.112.112#dns.quad9.net DNSOverTLS=yes DNSSEC=yes ``` ```bash sudo systemctl restart systemd-resolved # Verify resolvectl status resolvectl query example.com ``` ### macOS macOS 14 Sonoma added native encrypted DNS via profiles (`.mobileconfig`). For earlier versions, use a local DoT proxy like `dnscrypt-proxy` or `stubby`. ### Router (dnsmasq + stubby) Running DoT on a router benefits all devices on the LAN without per-device configuration: ```bash # Install stubby (DoT stub resolver) apt install stubby # /etc/stubby/stubby.yml (excerpt) resolution_type: GETDNS_RESOLUTION_STUB dns_transport_list: - GETDNS_TRANSPORT_TLS tls_authentication: GETDNS_AUTHENTICATION_REQUIRED upstream_recursive_servers: - address_data: 9.9.9.9 tls_auth_name: "dns.quad9.net" - address_data: 149.112.112.112 tls_auth_name: "dns.quad9.net" # Point dnsmasq to stubby # /etc/dnsmasq.conf server=127.0.0.1#8053 no-resolv ``` ## Running a Validating DoT Resolver with Unbound For operators who want to run their own DNS Resolver with DoT support, Unbound is the standard choice: ```conf # /etc/unbound/unbound.conf (DoT server excerpt) server: interface: 0.0.0.0@853 tls-service-key: "/etc/unbound/tls.key" tls-service-pem: "/etc/unbound/tls.crt" tls-port: 853 # Enable DNSSEC validation auto-trust-anchor-file: "/var/lib/unbound/root.key" forward-zone: name: "." forward-tls-upstream: yes forward-addr: 9.9.9.9@853#dns.quad9.net forward-addr: 149.112.112.112@853#dns.quad9.net ``` Obtain a TLS certificate from Let's Encrypt for your resolver's hostname. Renew it with certbot or acme.sh and reload Unbound (`unbound-control reload`) after each renewal. See Running Your Own DNS Server: BIND vs PowerDNS for a fuller guide to setting up and hardening a self-hosted DNS server. ## Testing DoT Connectivity ```bash # Test with kdig (from knot-dnsutils) kdig -d @9.9.9.9 +tls-ca +tls-hostname=dns.quad9.net example.com AAAA # Test with openssl s_client echo | openssl s_client -connect dns.google:853 -servername dns.google # Check which resolver your system is using resolvectl status # systemd-resolved scutil --dns # macOS ``` ## Performance and Latency The main concern with DoT is connection setup overhead. TLS 1.3 has a 1-RTT handshake, compared to zero overhead for plaintext UDP DNS. For a single query, this adds ~20-50ms depending on resolver proximity. In practice, DoT clients maintain persistent connections. Once the TLS session is established, subsequent queries over the same connection add no handshake overhead. The DNS Cache (TTL) at the resolver side remains equally effective. For long-lived connections (a smartphone on Wi-Fi, a server application), the per-query overhead of DoT is negligible. For latency-sensitive applications, choose a geographically close Managed DNS DoT resolver, or run Unbound locally (loopback DoT resolver) to eliminate network round-trip time for the TLS session entirely. ## DoT and Nameserver Selection DoT protects the last-mile query from your device to the recursive resolver. It does not protect the queries that the recursive resolver makes to Authoritative DNS Server servers on your behalf — those still travel as plaintext UDP/TCP (with DNSSEC providing integrity verification). A complete privacy stack requires both DoT/DoH for the stub-to-resolver leg and DNSSEC for data authenticity across the full recursive path. For performance considerations when running your own DoT resolver, see DNS Performance Optimization at Scale. For the full self-hosted resolver setup, see Running Your Own DNS Server: BIND vs PowerDNS. DNS Record Helper

Related Guides