Chapter 12 · Part IV · Trust & privacy

Encrypted DNS: DoT, DoH & DoQ

Every visit to a website starts with a question your device asks out loud: what is the address of this name? For most of the internet's life that question crossed the network in plain text, readable by the café Wi-Fi, your provider and anyone else on the path. This chapter is about who can read it, how DNS over TLS, HTTPS and QUIC seal it, and what they still leave in the open. If tunnels and encryption on the wire are new to you, start with networks · VPNs, tunnels & overlays; if you need a refresher on what a resolver is and how your device gets one, read networks · DHCP & DNS.

853the port for encrypted DNS without the web: TCP 853 for DNS over TLS, UDP 853 for DNS over QUIC
2018the year RFC 8484 put DNS inside ordinary HTTPS requests, as DNS over HTTPS (DoH)
application/dns-messagethe media type a DoH request and answer carry: the same DNS bytes as on port 53, unchanged

Common mix-up: encrypted DNS is not DNSSEC. Encryption protects the conversation between your device and one resolver, so nobody on that stretch of wire can read or change it. DNSSEC protects the data, so you can check that an answer really came from the zone's owner, however many hands it passed through. They solve different problems, and you can have either one without the other. Chapter 11 is about DNSSEC.

The postcard problem

Who can read a lookup

Look at it from the point of view of a single question. You type www.shop.example into a browser on a café's Wi-Fi. Before anything else happens, your device's stub resolver writes a small DNS query, perhaps 40 bytes, and sends it as one UDP packet to port 53 on the resolver it was given. The query contains the name, the record type and nothing else that is secret, but that is already a lot: the name of every site you visit, every app that phones home, every mail server your laptop checks, in order and with timestamps.

The DNS data itself is public. Anyone can look up www.shop.example, and the answer is the same for everyone. What is private is the fact that you asked, and when. RFC 9076, the IETF's survey of DNS privacy (it replaced the earlier RFC 7626 in 2021), sorts the people who can learn that fact into three groups, and the rest of this chapter is organized around them.

  • On-path observers. Anyone who carries your packets: the Wi-Fi operator, your internet provider, a transit network, a government tap. With classic DNS they read every query and every answer. They can also forge an answer, because nothing in a plain UDP reply proves who sent it.
  • The resolver operator. Your recursive resolver has to see the full question in order to answer it, and it sees your IP address. Encryption on the wire does nothing about this party: you are encrypting to it.
  • The authoritative servers. On a cache miss, the resolver asks the root, then the top-level domain, then the zone's own servers. They see the resolver's address rather than yours, but historically each of them was sent the whole name.

The cheapest fix for the third group needs no encryption at all. QNAME minimisation (RFC 9156) has the resolver tell each authoritative server only as much of the name as that server needs to give a referral: the root is asked about example, the example servers about shop.example, and only the zone that actually holds the record hears www.shop.example. Chapter 3 walks through how that works step by step. The first two groups need cryptography, and that is where DoT, DoH and DoQ come in.

ObserverWith classic DNS (Do53)With DoT, DoH or DoQ to your resolverWhat else helps
Wi-Fi, ISP, transitEvery name, type and answer, your IP, timing; can forge repliesYour IP, the resolver's IP, timing and sizes; cannot read or forgePadding (sizes), ECH (the later TLS connection)
Your resolverEvery name, your IPEvery name, your IP (unchanged)Choosing whom to trust; Oblivious DoH
Root and TLD serversThe full name, the resolver's IPUnchanged: that hop is a separate conversationQNAME minimisation; resolver-to-authoritative encryption
The zone's own serversThe full name, the resolver's IPUnchangedNothing: it has to know what you asked to answer it
Go deeper: what else leaks in a plain query

RFC 9076 is careful to list leaks that are easy to miss. The timing of queries is a fingerprint: a news site triggers a burst of lookups for its ad and analytics hosts in a recognizable order. The source port and query ID of a stub can make one device stand out behind a shared address. Some resolvers forward part of your IP address to authoritative servers in an EDNS Client Subnet option so that a CDN can pick a nearby server for you (chapter 9), which hands a slice of your location to parties you never chose.

There is also cache snooping: anyone who can query a shared resolver can ask, with recursion turned off, whether a name is already in its cache, and so learn whether someone nearby looked it up recently. Resolvers that serve the public usually blunt this. RFC 8932 collects recommended practices for privacy-respecting resolver operators, including how long to keep logs and what to publish in a privacy policy.

Instrument 1

Who sees what

Pick a transport and switch the other protections on and off. The diagram shows each stretch of the path as readable (dashed) or encrypted (solid), and the cards below list, in words, exactly what each party learns about one lookup of www.shop.example from a laptop at 198.51.100.23. All names and addresses are simulated documentation values. The model assumes the resolver's cache is empty; on a cache hit the authoritative servers learn nothing at all.

TCP port 853

DNS over TLS: a sealed pipe

The first standard answer, DNS over TLS or DoT (RFC 7858, May 2016), is the most direct one. Take the DNS-over-TCP that has existed since the 1980s, where each message is preceded by a two-byte length, and run it inside TLS, the same encryption that protects HTTPS. Give it its own well-known port, TCP 853, so that a client knows to start TLS immediately instead of guessing. Nothing about the DNS message changes. The resolver unwraps the TLS layer and sees exactly the bytes chapter 2 takes apart.

TLS buys two things. Confidentiality: an observer sees a TLS session to port 853 and the sizes and timing of the records inside it, but not the questions. Integrity and authentication: if the client checks the server's certificate, a box in the middle can no longer slip in a forged answer, because it cannot produce valid TLS records for that session. That second half only works if the client actually checks, and that turns out to be the hard part.

A stub resolver usually learns its resolver as a bare IP address, from DHCP or router advertisements. A certificate is issued to a name. So what should the client check the certificate against? RFC 8310 (March 2018) answers with two usage profiles:

  • Strict privacy. The client is configured with an authentication name, such as dns.example.net, or a key pin, and refuses to send any query unless the server proves it holds that name. If it cannot, resolution fails. That is safe and unforgiving: a captive portal at a hotel can break name resolution until you sign in another way.
  • Opportunistic privacy. The client tries TLS on port 853. If the handshake works it uses it, ideally authenticating but carrying on if it cannot; if port 853 is blocked it falls back to plain port 53. This defeats a passive eavesdropper and costs nothing, but an active attacker who blocks port 853 or presents any certificate wins quietly.

Android's "Private DNS" setting is the most widely seen example of this split: give it a hostname and it runs strict DoT to that name; leave it on automatic and it tries the network's resolver over TLS opportunistically.

Laptop Resolver, TCP 853 1 · TCP: SYN → ← SYN-ACK 2 · TLS 1.3: ClientHello → ← ServerHello + certificate 3 · sealed: length + DNS query → ← answer roundtrips
Go deeper: keeping the pipe open, and the DTLS that nobody uses

Three round trips for the first answer would be painful if every query paid them. RFC 7858 therefore expects clients to keep the connection open and send many queries down it, and to pipeline them: send the next query before the previous answer arrives, and match answers by ID, since the server may answer out of order. TLS session resumption lets a client that reconnects later skip most of the handshake. In practice a DoT stub keeps one long-lived connection to its resolver, and after the first lookup each query costs a single round trip, like classic UDP.

There was also a datagram variant, DNS over DTLS (RFC 8094, experimental), meant to keep DNS on UDP. It saw almost no deployment and has been overtaken by DNS over QUIC, which does the same job better.

Port 443, like everything else

DNS over HTTPS: hiding in the crowd

A dedicated port has a weakness: it is easy to see, and easy to block. A network that wants to force everyone onto its own resolver can drop TCP 853 and most opportunistic clients will quietly fall back to plain text. DNS over HTTPS, DoH (RFC 8484, October 2018), takes the opposite approach. It sends each DNS message as an ordinary HTTPS request to a URL such as https://doh.example/dns-query. On the wire that is TLS to port 443, the same as every web page. A network can still block a well-known DoH server by its address, but it cannot block DoH as a protocol without blocking the web.

The message itself is still the binary DNS message, unchanged, labeled with the media type application/dns-message. There are two ways to send it:

  • POST: the message bytes are the request body. Simple and compact.
  • GET: the message is encoded with base64url (the URL-safe alphabet from RFC 4648, with - and _ in place of + and /, and no trailing =) and placed in a URL parameter called dns. That makes the whole question part of the URL, so an ordinary HTTP cache can store the answer and hand it to the next person who asks the same question.

For GET caching to work, two identical questions have to produce identical URLs. That is why RFC 8484 says a DoH client SHOULD set the DNS message ID to 0: the ID was there to match a UDP answer to its question, and HTTP already does that job. The answer's cache lifetime comes from the smallest TTL in it, carried in the HTTP freshness headers. The DNS header carries the type; the HTTP layer carries everything else.

Because it is just HTTPS, DoH slots into software that already speaks HTTP. A browser can resolve names over the same HTTP/2 or HTTP/3 connection machinery it uses for pages, without asking the operating system. That is the source of both its popularity and its controversy, which the trade-offs section below comes back to. RFC 8484 recommends HTTP/2 as the minimum version, so many queries share one connection without waiting on each other.

GET /dns-query?dns=AAABAAABAAAAAAAAA3d3dwdleGFtcGxlA2NvbQAAAQAB
The worked example from RFC 8484, section 4.1: a query for the A record of www.example.com, with ID 0 and recursion desired, is 33 bytes of DNS message and 44 characters of base64url. Instrument 2 rebuilds it byte by byte.
Instrument 2

Build a DoH request

Type a name and choose a type. The builder writes the real DNS query bytes (the header, the question, and optionally an EDNS OPT record), encodes them with base64url, and shows both the GET URL and the POST request a DoH client would send. Turn on padding and the message grows to the next multiple of 128 bytes, as RFC 8467 recommends for queries, so that the length no longer hints at the name. With the defaults, the output matches the example printed in RFC 8484. Nothing is sent anywhere: doh.example is a reserved name and the page makes no network requests.

header, 12 bytesquestionEDNS OPT recordpadding option
DNS message–
base64url length–
GET path length–
Header decoded–
GET form
POST form

      

UDP port 853

DNS over QUIC: one handshake, many streams

DoT and DoH both ride on TCP, and TCP has two costs that classic DNS never paid. The first is set-up: a TCP handshake, then a TLS handshake, before the first question can go. The second is head-of-line blocking: TCP delivers bytes strictly in order, so if one packet is lost, every answer queued behind it waits for the retransmission, even answers that have nothing to do with the lost one.

QUIC (RFC 9000) is a transport built on UDP that fixes both. It combines the transport and TLS 1.3 handshakes into a single round trip, and it carries many independent streams in one connection, each delivered in order on its own, so a loss on one stream does not stall the others. HTTP/3 runs on it. DNS over QUIC, DoQ (RFC 9250, May 2022), puts DNS directly on QUIC on UDP port 853, with each query and its answer on a stream of their own. As with DoH, the DNS message ID must be 0, since the stream already pairs question and answer.

DoQ is also designed for more than the stub-to-resolver hop. The RFC explicitly covers resolver-to-authoritative traffic and zone transfers, places where a long-lived, low-latency encrypted connection with no head-of-line blocking is attractive. A client that has talked to the server before can use QUIC's 0-RTT resumption to send its first query in the very first packet, at the cost of some replay and privacy caveats the RFC spells out.

TransportRFCCarried overPortMessage IDStatus
Do531035UDP, TCP for large answers53randomClassic DNS; plain text
DoT7858, 8310TLS over TCPTCP 853randomStandards Track
DoH8484HTTPS (HTTP/2 or later recommended)TCP or UDP 4430 (SHOULD)Standards Track
DoQ9250QUICUDP 8530 (MUST)Standards Track
ODoH9230HTTPS through a proxy, sealed with HPKE4430Experimental
Instrument 3

What encryption costs in time

This is a round-trip model, not a measurement. Set the round-trip time to your resolver and the number of lookups a page needs. The model counts handshakes and query round trips for a fresh connection, then charges one round trip for every later query on the kept-open connection. Turn on resumption to see what TLS 1.3 early data and QUIC 0-RTT save on a reconnect. Each bar is labeled with its arithmetic below; packet loss, server time and congestion are left out.

    Go deeper: why the model is kinder than reality, and harsher

    Kinder: a lost packet in the TCP handshake costs a retransmission timeout, often a second or more, and with TCP every later answer waits behind any lost segment. Classic UDP DNS has its own version of this, a client retry timer, usually a second or more. QUIC recovers loss per stream. None of that is in the bars.

    Harsher: real clients rarely pay the full handshake. A DoH client usually keeps its connection to the resolver warm for minutes, and a browser may resolve over a connection it already has open to the same provider. After that the cost of encrypted DNS is one round trip per uncached question, the same as UDP, plus a few dozen bytes of framing and encryption overhead per message.

    Split the secret

    Oblivious DoH: nobody knows both

    Encryption to the resolver moves your trust; it doesn't remove it. Your resolver still learns your address and every name you look up. You can choose a resolver whose privacy policy you like, but you have to take the policy on faith. Oblivious DNS over HTTPS, ODoH (RFC 9230, June 2022, published as Experimental), arranges things so that no single party has to be trusted with both facts.

    There are two servers, run by different organizations. The client looks up the target resolver's public key and seals its DNS query for that key using HPKE, hybrid public-key encryption. It then sends the sealed query, over ordinary HTTPS, to an oblivious proxy, with the media type application/oblivious-dns-message. The proxy can see the client's IP address but only a sealed blob; it forwards the blob to the target. The target opens it, resolves the name and seals the answer with a key only that client can use, but it only ever sees the proxy's address.

    The guarantee depends on the two operators not colluding, which is a policy assumption rather than a mathematical one, and on enough clients sharing the proxy that a single user cannot be picked out by timing. The cost is an extra hop on every query. The same split-trust idea, generalised to any HTTP request, became Oblivious HTTP (RFC 9458).

    Client 198.51.100.23 Proxy organization A Target organization B HTTPS + sealed query HTTPS + same sealed query Proxy knows WHO (your IP), not WHAT Target knows WHAT, not WHO You know both Simulated addresses. Privacy holds only if A and B do not share logs.
    Finding an encrypted resolver

    Discovery: from an IP address to a name you can verify

    Encryption is useless if the client doesn't know that its resolver supports it, or under which name to authenticate it. For years the only answers were manual configuration or hard-coded lists in browsers and operating systems. Two standards from November 2023 let a network advertise its encrypted resolver properly.

    Discovery of Designated Resolvers, DDR (RFC 9462), works from the resolver the client already has. The client asks that resolver, over plain DNS, for the SVCB records at the special name _dns.resolver.arpa. The answer lists the encrypted endpoints the resolver operator designates: their names, ALPN protocols (dot, h2, h3, doq), ports, and for DoH the URI path. In verified discovery, the client then connects to the designated name and requires that the TLS certificate cover the original resolver's IP address too, which proves that whoever runs the advertised server also controls the unencrypted one. If the original resolver is at a private address, no public certificate can cover it, so only opportunistic discovery is possible, and only when the designated resolver has that same IP address.

    ;; DDR: a client asks its plain resolver for its designated encrypted resolvers (simulated)
    ;; QUESTION SECTION:
    ;_dns.resolver.arpa.          IN  SVCB
    
    ;; ANSWER SECTION:
    _dns.resolver.arpa.  7200  IN  SVCB  1 dns.example.net. alpn="h2,h3" port=443 dohpath="/dns-query{?dns}"
    _dns.resolver.arpa.  7200  IN  SVCB  2 dns.example.net. alpn="dot" port=853
    _dns.resolver.arpa.  7200  IN  SVCB  3 dns.example.net. alpn="doq" port=853
    
    ;; ADDITIONAL SECTION:
    dns.example.net.     7200  IN  A     192.0.2.53
    ;; verified discovery: the TLS certificate of dns.example.net must also list 192.0.2.53

    Discovery of Network-designated Resolvers, DNR (RFC 9463), carries the same information in the network's own configuration messages: new DHCPv4, DHCPv6 and IPv6 Router Advertisement options that hand the client an authentication name, protocol list and addresses at the moment it joins. That removes the first plain-text query DDR needs, but it trusts the local network's configuration channel, which is no stronger than the network itself.

    The last stretch is the one from the resolver up to the authoritative servers. It is still almost entirely plain UDP, which matters less than the first hop because the resolver aggregates many users, but it is not nothing. RFC 9539 (February 2024, Experimental) describes how a resolver can, without any coordination, try DoT or DoQ to an authoritative server's address, remember which servers answered encrypted, and fall back silently when they don't. It defends against passive monitoring only; an active attacker can force the fallback. It is a deliberately modest first step, because authenticating authoritative servers at scale is an open problem.

    Go deeper: why not just put it in the resolver's IP address?

    The awkward fact behind all of this is that DHCP and router advertisements hand out resolvers as addresses, and TLS authenticates names. Public resolver operators solved it for themselves by getting certificates that list their well-known anycast addresses as IP subject names, so a client configured with an address can still check a certificate. That is what makes DDR's verified mode possible for them. A home router at a private address can't do the same, which is why DNR exists and why so many devices still run opportunistic DoT, or nothing, on home networks.

    Browsers took a third route: a built-in list of resolvers known to support DoH, and an upgrade to the matching DoH endpoint when the system resolver's address is on the list. DDR standardized that idea so that it no longer needs a hand-maintained list.

    The limits

    What encryption still shows

    Encrypted DNS closes one window. Several others stay open, and it helps to name them, because the most common overclaim about DoH is that it hides which sites you visit. It does not, on its own.

    • Destination addresses. After the lookup, your device connects to the site's IP address, and every packet carries it. On shared hosting and big CDNs, one address serves thousands of sites, so the address alone is a weak clue; for a site on its own server it is a strong one.
    • The server name in TLS. The opening message of a TLS connection, the ClientHello, normally names the site in plain text in the Server Name Indication field, so the server knows which certificate to present. Encrypted Client Hello (ECH) encrypts that name under a key the site publishes in its DNS HTTPS record (chapter 5), leaving only a shared public name visible. Note the dependency: ECH is only private if the lookup that fetched its key was private too.
    • Sizes and timing. Encrypted messages keep roughly their original length. A name like a.example and one with a 60-character label produce visibly different packet sizes. EDNS padding (RFC 7830 defines the option, RFC 8467 the policy) rounds queries up to a multiple of 128 bytes and answers to a multiple of 468, which blurs that signal. Timing patterns remain.
    • The resolver, and what it keeps. As above: encryption to a resolver is a decision to trust that resolver.
    • Whether the answer is true. Authentication of the transport tells you that you are talking to the resolver you meant. It says nothing about whether that resolver was fed a forged answer upstream, or chose to rewrite one. Only DNSSEC validation (chapter 11) proves that an answer came from the zone's owner.
    Go deeper: the padding arithmetic

    RFC 8467 weighed several policies and recommended block-length padding: clients pad each query to the closest multiple of 128 octets, and servers pad each answer to a multiple of 468. The query size was chosen because nearly all queries fit in one 128-byte block, so they all look the same. The 468 figure was chosen so that three blocks, 1,404 bytes, still fit in a typical 1,500-byte Ethernet packet after IP, UDP or TCP and encryption overhead. Padding only makes sense on an encrypted transport; in plain text it just wastes bytes, and the RFC says not to do it there.

    Padding costs bandwidth and helps only against size analysis. It doesn't hide how many queries you made or when. The padding option itself is EDNS option code 12, filled with zero bytes, which Instrument 2 builds.

    Who loses what

    The trade-offs: centralisation, filters and split horizon

    Plain DNS had an accidental property: whoever ran the network could see and steer it. Encrypted DNS removes that property, and some people relied on it on purpose.

    • Network filtering. Schools, companies and parents often block malware and adult sites at the resolver they hand out. An application that sends DoH to a resolver of its own choosing quietly walks around that filter. The defensive answer is not to break encryption but to offer it: run an encrypted resolver, advertise it with DDR or DNR, and manage devices by policy rather than by watching the wire.
    • Security monitoring. Defenders use DNS logs to spot infected machines calling home. When applications resolve over DoH to outside servers, those queries disappear from the network's view. Managed networks typically allow encrypted DNS only to their own resolver and log there.
    • Split horizon. Many organizations answer internal names, such as intranet.corp.example, only from their internal resolvers, and some names resolve differently inside and out (chapter 10). A device that sends everything to a public resolver gets the outside view, or an NXDOMAIN, and internal sites stop working. Browsers handle this with fallback rules and enterprise policies; that is fragile, which is another argument for discovering the local resolver rather than bypassing it.
    • Centralisation. When one browser ships one default DoH provider, a large share of the world's lookups can end up at a handful of operators. That trades many small, leaky observers for a few large, well-defended ones. Whether that is an improvement depends on whom you trust more, which is why the standards work moved toward discovering the network's own encrypted resolver.

    Platforms expose this as a setting. Firefox, for example, documents several DoH protection levels, from off through a default that can fall back to the system resolver, to a strict mode that never does, and it turns DoH off by default on networks that signal they filter, using a "canary" domain the network can make fail. Mozilla's support page describes the current levels and exceptions. Public resolvers such as Cloudflare's 1.1.1.1 and Google Public DNS publish their DoH and DoT endpoints and privacy commitments in their documentation (sources below).

    ConcernDoT (port 853)DoH (port 443)DoQ (UDP 853)
    Easy for a network to spotYes: its own portHard: looks like web trafficYes: its own port
    Fits in the OS stub resolverNatural fitCommon, but often app-levelNatural fit; newer
    Head-of-line blocking on lossYes (TCP)Yes on HTTP/2; no on HTTP/3No
    Answers cacheable by HTTP cachesNoYes, with GET and ID 0No
    Resolver-to-authoritative useYes (RFC 9539)RareYes (RFC 9250, 9539)
    Worked example

    Checking an encrypted resolver by hand

    Modern versions of dig can speak DoT and DoH directly, which is the quickest way to see that a resolver answers over an encrypted transport. The commands and trimmed output below are illustrative, against a simulated resolver; substitute the name your provider documents. The output is the same DNS message you would get on port 53. Only the transport changed, and dig tells you which one it used on the SERVER line.

    $ dig +tls @dns.example.net www.example.com A
    ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 0
    ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
    www.example.com.     300  IN  A   203.0.113.80
    ;; SERVER: 192.0.2.53#853(dns.example.net) (TLS)
    
    $ dig +https @dns.example.net www.example.com A
    www.example.com.     300  IN  A   203.0.113.80
    ;; SERVER: 192.0.2.53#443(dns.example.net) (HTTPS)
    
    $ dig +tls @192.0.2.99 www.example.com A
    ;; Connection to 192.0.2.99#853(192.0.2.99) for www.example.com failed: connection refused.
    ;; a strict client stops here; an opportunistic one would retry on port 53

    Two details are worth noticing. The ID is 0 in the DoH case, as RFC 8484 recommends, and the port changed from 853 to 443 without anything else in the answer changing. And the failed case is the strict-versus-opportunistic decision of RFC 8310 in miniature: what your client does next is a policy choice, not a protocol one.

    Cheat sheet

    Terms from this chapter

    Do53
    Classic, unencrypted DNS on UDP or TCP port 53. Readable and forgeable by anyone on the path.
    DoT
    DNS over TLS (RFC 7858): DNS messages, each with a two-byte length, inside a TLS session on TCP port 853.
    DoH
    DNS over HTTPS (RFC 8484): DNS messages sent as HTTPS requests with media type application/dns-message, by GET or POST.
    DoQ
    DNS over QUIC (RFC 9250): DNS on QUIC streams on UDP port 853, one stream per query, one-round-trip handshake.
    ODoH
    Oblivious DoH (RFC 9230, experimental): queries sealed for a target resolver and relayed by a separate proxy, so neither learns both who asked and what.
    Strict privacy profile
    RFC 8310 usage profile: only send queries to a resolver that proves its configured name; fail otherwise.
    Opportunistic privacy profile
    RFC 8310 usage profile: use encryption when it works, fall back to plain DNS when it doesn't. Beats passive snooping, not active attackers.
    DDR
    Discovery of Designated Resolvers (RFC 9462): ask your resolver for SVCB records at _dns.resolver.arpa to learn its encrypted endpoints.
    DNR
    Discovery of Network-designated Resolvers (RFC 9463): DHCP and Router Advertisement options that announce an encrypted resolver.
    EDNS padding
    An EDNS option (code 12, RFC 7830) that fills encrypted DNS messages with zero bytes to standard block sizes so their length leaks less.
    base64url
    The URL-safe base64 alphabet (RFC 4648) DoH uses to put a binary DNS message in a GET URL: - and _ instead of + and /, no = padding.
    Where this comes from

    Sources