Most DNS-over-HTTPS guides say the overhead is "negligible after warmup" and stop there. I wanted the number, so tonight I timed it.
Setup
- Vantage point: one VPS in a datacenter (Contabo, AS51167, Lauterbourg, France), 1 Nov 2026, 22:0x UTC. A datacenter sits much closer to these resolvers than a home connection, so read the absolute values as a floor, not as what you will see on Wi-Fi.
- Resolvers: Cloudflare (
cloudflare-dns.com/dns-query), Google (dns.google/dns-query), Quad9 (dns.quad9.net/dns-query). - Query:
example.com A, RFC 8484 wire format over HTTP/2, curl 8.5.0. - 3 runs per resolver. Each run opens one connection and sends 5 queries on it, so query 1 pays for TCP + TLS and queries 2 to 5 reuse the connection. 45 queries in total.
Q="AAABAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE" # example.com A, base64url
u="https://cloudflare-dns.com/dns-query?dns=$Q"
curl -s --http2 -H 'accept: application/dns-message' \
-w "total=%{time_total} connect=%{time_connect} tls=%{time_appconnect}\n" \
"$u" -o /dev/null "$u" -o /dev/null "$u" -o /dev/null "$u" -o /dev/null "$u" -o /dev/null
Results
| Resolver | 1st query (3 runs) | TLS done at | Queries 2 to 5, median (12 values) | Plain UDP, dig (5 values) |
|---|---|---|---|---|
| Cloudflare | 47.6 / 28.3 / 28.9 ms | 40.0 / 23.8 / 24.1 ms | 4.8 ms | 2, 3, 3, 3, 4 ms |
| 36.1 / 39.0 / 49.9 ms | 26.9 / 26.7 / 41.2 ms | 5.6 ms | 4, 5, 7, 8, 18 ms | |
| Quad9 | 39.8 / 38.0 / 52.4 ms | 32.2 / 30.4 / 37.5 ms | 7.7 ms | 2, 2, 2, 12, 13 ms |
What the numbers say:
- The first query costs 28 to 52 ms, and almost all of it is the handshake. TCP connect is 4 to 16 ms; the TLS handshake finishes 24 to 41 ms after the start. The DNS answer itself is the small remainder.
- Once the connection is open, a DoH query costs 4 to 15 ms, a few milliseconds above plain UDP to the same provider. The "negligible after warmup" claim holds, on this path.
- Quad9's third run settled at 15 ms per warm query instead of 7.7. Same connection, same query: most likely a different anycast node or a longer route for that connection. One run is not enough to rank resolvers on speed; three already show the spread.
-
digquery time and curltime_totaldo not measure exactly the same thing (curl also counts HTTP framing and its own processing). Compare inside a column, not across columns.
The practical consequence
The cost of DoH is per connection, not per query. A browser keeps its DoH connection open, so you pay the 30 to 50 ms once and then a few ms per lookup. Anything that keeps tearing that connection down (aggressive power saving, a flaky network, a VPN client that resets sockets on reconnect) pays the handshake again each time.
One filtering check, while I was there
Quad9 publishes isitblocked.org as a canary for its blocklist. From the same VPS:
dig @9.9.9.9 isitblocked.org -> NXDOMAIN (blocked)
dig @1.1.1.1 isitblocked.org -> 74.208.236.124
dig @1.1.1.2 isitblocked.org -> 74.208.236.124
So the filter is live on Quad9's plain resolver. That 1.1.1.2 (Cloudflare's malware-filtering resolver) does not block it says nothing about which blocklist is better: it is Quad9's own test domain, not malware.
Limits
One vantage point, one domain already cached at every resolver, one evening. No home ISP path, no mobile network, no HTTP/3. If you rerun the snippet from home, I would expect the handshake to grow far more than the warm queries.
If you are setting DoH up rather than benchmarking it, I keep a browser-by-browser guide on my own site (Chrome, Edge, Firefox, Safari, iOS, Android) that also covers the most common leak, browser DoH silently bypassing a VPN's resolver: DNS over HTTPS setup per browser.
Top comments (0)