TL;DR

  • Quad9 answers both protocols on one address, 149.112.112.112 - DNSCrypt on port 8443, DoT on 853 - so the protocol is the only thing that varies.
  • A cold DNSCrypt lookup costs more bytes than a TLS 1.3 handshake, in half the packets. DNSCrypt: 10 packets, 8,953 bytes. DoT: 20 packets, 7,166.
  • The direction reverses. DoT is downhill - the client sends 2,241 bytes and receives 4,924, most of it the certificate chain. DNSCrypt is uphill: 8,324 sent, 629 received.
  • In steady state the gap is 5.9x and it does not close: 264 bytes per DoT lookup against 1,564 for DNSCrypt, because every DNSCrypt query goes out padded to exactly 1,280 bytes.
  • Measured at the wire, not the clock, because no single client speaks both protocols.

Why packets and not milliseconds

dnscrypt-proxy does DNSCrypt and DoH but not DoT. q does DoT, DoH and DoQ but not DNSCrypt. Timing one against the other would have compared two programs rather than two protocols, which is a mistake we have made and published before. Packets and IP-layer bytes do not care which program produced them, so that is what was counted: tcpdump on the egress interface, filtered to the one resolver address, with the loopback hop between dig and dnscrypt-proxy outside the measurement entirely.

The padding is the whole difference

DNSCrypt opens by sending three certificate probes, two of them padded to 3,268 bytes and IP-fragmented into three packets each. Only the 508-byte probe was answered, in all 20 captures. After that every query is padded to 1,280 bytes and every answer comes back in steps of 64. That padding is not waste - it is what stops an observer sizing your queries - but it is paid on every lookup, where a handshake is paid once.

One claim on the page now has a number behind it as well. Of the 452 DNSCrypt entries in the signed public resolver list, 420 use port 443, so "usually rides 443" holds; Quad9 is one of the 18 exceptions on 8443.

What was thrown away

Nine of 20 cold DNSCrypt runs are excluded: the capture window closed before the answer landed, an artefact of the harness restarting the proxy inside the capture rather than anything the protocol did. The DoT answer also first measured as two clean values, 148 bytes in 16 runs and 1,500 in two. The two were not large answers - they were the server splitting its certificate across two TCP segments, and a classifier that read any server payload as the answer. Read properly it is 148 bytes in every run. A measurement that produces two tidy values deserves more suspicion than one that produces noise.

Full write-up, with the method and the ranges: DoH vs DoT vs DNSCrypt vs DoQ: Which to Choose.