Back to guides
Verification

A Leak Test Cannot Tell You Whether Your DNS Was Encrypted

It reports which resolver answered you. That is a useful thing to know and it is not the same question, because encryption happens on the way there and a leak test never sees that leg.

26 August 2026 · every output below measured on the day

Two different questions

You set a DNS-over-HTTPS URL somewhere, then run a leak test, and it names the resolver you configured. It feels like confirmation. It is confirmation of one thing only.

A leak test works by asking you to resolve names it controls, then reporting which addresses queried its authoritative servers. What reaches it is the recursive resolver that did the lookup. It has no view of the hop between your device and that resolver - which is exactly the hop encryption protects. The two questions are:

  • Which resolver answered? A leak test answers this well. It is the right tool for finding out that your VPN or router quietly redirected you somewhere else.
  • Was the query encrypted in transit? A leak test cannot answer this at all. Plaintext UDP port 53 to a resolver and DNS-over-HTTPS to the same resolver produce an identical leak-test result.

Both configurations are "not leaking". Only one of them is private on the local network, which is where the observer you were worried about usually sits. If you want the first question answered, our leak test and the guide to how a leak test actually works cover it. The rest of this page is the second question.

Your device browser or OS Resolver does the lookup Authoritative the leak test's own Was it encrypted? this leg, and only this leg a leak test never observes it Which resolver answered? visible from the far end this is what a leak test reports Plaintext port 53 and DoH to the same resolver give the same leak-test result. The questions are independent: a setup can pass one and fail the other. encrypted, not leaking | encrypted, leaking plaintext, not leaking | plaintext, leaking

Four combinations exist. A leak test distinguishes two of them.

Check DNS over HTTPS from the command line

The direct test is to speak the protocol yourself. A DoH query is an HTTP request carrying a wire-format DNS message, so curl is enough. This asks for example.com:

curl -s -o /dev/null -w 'http=%{http_version} code=%{http_code}\n' \
  -H 'accept: application/dns-message' \
  'https://dnsdoh.art/dns-query?dns=AAABAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE'
http=2 code=200

code=200 means the endpoint answered a real DNS query over TLS. That base64 string is a fixed, standard query for example.com A - it is the same for every server, so you can point this at any DoH URL and change nothing else.

Two failure modes are worth recognising. A 400 usually means the server is a DoH endpoint but disliked the query. Anything that hangs or returns 000 means you did not reach a DoH server at all, which includes the common case of a captive portal or filtering middlebox intercepting 443.

Do not test a DoH endpoint with curl -I. A bare HEAD /dns-query against our endpoint returns nothing at all and curl exits 92. That is the endpoint declining a request with no query in it, not a broken server. We wasted a check on exactly this. Always send the ?dns= parameter.

Check DoT and DoQ

DNS over TLS and DNS over QUIC are not HTTP, so curl cannot speak them. The most convenient client is q, which takes the transport in the server URL:

# DNS over TLS, port 853
q -s tls://dnsdoh.art example.com A

# DNS over QUIC, port 853/udp
q -s quic://dnsdoh.art example.com A
example.com. 5m A 104.20.23.154
example.com. 5m A 172.66.147.243

An answer is the whole test. If the transport were unavailable you would get a connection error rather than records, because there is no fallback to plaintext in either protocol.

If you reach for kdig instead and it reports handshake failed (The requested data were not available.), that is a client bug rather than a server problem: versions before Knot 3.4.7, including the 3.3.4 in Ubuntu 24.04, abort against certificates issued with no Subject field. Ours is one such certificate and it verifies cleanly with gnutls-cli and openssl s_client.

Check DoH over HTTP/3

DoH3 is the same DoH request carried over QUIC instead of TCP. One flag separates them, and the useful part of the output is the version curl reports back:

curl -s --http3 -o /dev/null -w 'http=%{http_version} code=%{http_code}\n' \
  -H 'accept: application/dns-message' \
  'https://dnsdoh.art/dns-query?dns=AAABAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE'
http=3 code=200

http=3 is the confirmation. A plain curl without the flag will report http=2 even against a server that supports HTTP/3, because the first connection has no reason to try QUIC yet. Not every curl build can do this: it needs an HTTP/3-capable build, which curl --version will list.

How a browser gets there without a flag is the alt-svc header, which the endpoint returns on an ordinary query:

alt-svc: h3=":443"; ma=86400

Check it inside the browser

A browser's DNS setting is separate from the operating system's, so the browser is the only place that can tell you what the browser is doing. Both major engines report it, and neither puts it where you would guess.

Firefox states it outright. Settings, then Privacy & Security, scroll to DNS over HTTPS. It shows a Status line reading Active, Off, or Not active with a reason, and a Provider line naming the resolver in use. A reason attached to "Not active" is the most useful diagnostic in either browser, because it tells you why Firefox fell back.

Chrome, Edge and Brave have no such status page. What they do have is the DevTools Security panel, which describes the connection to the current site: "The connection to this site is encrypted and authenticated using ..." followed by the protocol, the key exchange and the cipher. That tells you about the page you are looking at rather than about DNS, but it is where you confirm whether a connection got Encrypted ClientHello, which the panel names directly.

Browser settings cover the browser only. Turning on Secure DNS in Chrome does nothing for your mail client, your package manager, or anything else on the machine. If you want the whole device covered, the setting belongs in the operating system or the router - the setup page has the per-platform steps.

Check ECH, and what it hides

Encrypted DNS hides which names you look up. It does not hide the name you then connect to, because that name is sent in the clear in the TLS handshake unless Encrypted Client Hello is in use. Checking whether a site offers ECH is a DNS lookup, since the key is published in the HTTPS record:

dig +short HTTPS dnsdoh.art
1 . alpn="h3,h2,http/1.1" ech=AEH+DQA9AgAgACBJKPwET/KL8YQLoeVc4Gl306d0v
K1FgBb4llpxekrUEQAIAAEAAQABAAMACmRuc2RvaC5hcnQAAA==

The presence of an ech= parameter is the answer: this name publishes an ECH key and a browser that supports ECH will use it. Decoding that blob gives version 0xfe0d, config id 2, an X25519 key of 32 bytes, and the public name dnsdoh.art - the name that appears in the clear instead of the real one.

The alpn parameter in the same record is worth reading while you are there. It advertises HTTP/3, HTTP/2 and HTTP/1.1, which is how a client knows to try QUIC before making a connection at all.

A flag existing is not a feature existing. curl documents --ech, and on this machine using it prints the installed libcurl version does not support this. The option is compiled in conditionally. Check what your build actually has with curl --version before concluding anything about the server.

Check what a device would discover on its own

Modern clients can find an encrypted endpoint without being told, using Discovery of Designated Resolvers. If you want to see what a device is offered before it commits, ask the resolver directly:

dig +short SVCB _dns.resolver.arpa @194.180.189.33
1 dnsdoh.art. alpn="h3,h2" port=443 ipv4hint=194.180.189.33 key7="/dns-query{?dns}"
2 dnsdoh.art. alpn="doq" port=853 ipv4hint=194.180.189.33
3 dnsdoh.art. alpn="dot" port=853 ipv4hint=194.180.189.33

Three designations, in the order the operator recommends them: DoH over HTTP/3 or HTTP/2 first, then DoQ, then DoT. The client picks from what it supports, which is why a plain address can upgrade itself on Windows and stays plaintext on a device with no DDR support. key7 is the dohpath, the URL template a client fills in.

Three ways to get a confident wrong answer

Each of these has cost us a wrong conclusion at some point, and all three look like a server fault when they are not.

  • Your TLS client cannot do what you are testing for. The openssl in Ubuntu 24.04 is 3.0, which has no ML-KEM. Point it at an endpoint that offers post-quantum key exchange and it negotiates the classical group and reports exactly that - Server Temp Key: X25519 - which reads as "the server does not support it" and means "I do not support it".
  • You tested the wrong verb. A DoH endpoint that ignores HEAD is not down. Send a real query.
  • You tested the wrong layer. A page that loads over HTTP/3 tells you nothing about DNS, and a DNS setting inside a browser tells you nothing about the rest of the machine. Match the test to the layer you changed.

There is a general shape here: a negotiated protocol has two ends, and a test only ever reports the intersection. Before concluding anything about the far end, check that your own end is capable of the thing you are looking for.

What our own pages report

Two of them are useful here, and it is worth being precise about their limits.

The leak test answers the first question only: which resolvers performed your lookups. That is the right tool for catching a VPN or router redirect, and it is not evidence about encryption, for the reason this page opens with.

The connection page reports the properties of your current connection to this site: the HTTP version, the TLS version and cipher, your address and network, and the headers your client sent. It is the quickest way to confirm that a browser really is reaching a site over HTTP/3, or to see what a request discloses. It does not report the key exchange group or ECH status, so for those use the commands above.

The short version

Question What answers it
Which resolver answered?a leak test
Is DoH reachable?curl with ?dns= , expect code=200
Is it really HTTP/3?curl --http3 , expect http=3
Do DoT and DoQ work?q -s tls:// and q -s quic://
Is my browser using DoH?Firefox status line; Chrome has none
Does this site offer ECH?dig +short HTTPS name , look for ech=
What would a device discover?dig SVCB _dns.resolver.arpa @resolver

If a single check has to stand in for all of them, make it the DoH one with a real query, because it is the only one that fails loudly when something in the middle is interfering. A test that cannot fail is not a test, and "the leak test looked fine" is the encrypted-DNS version of that.