TL;DR

  • ECH's key is published in DNS, so dig <name> HTTPS +short tells you whether a site can do ECH at all. Of eight names checked, three publish a key, three publish the record without one, and two have no HTTPS record at all.
  • cloudflare.com is in the middle group. Its own test host crypto.cloudflare.com publishes a key; the main name does not.
  • Neither DNS tool shipped with Windows can fetch an HTTPS record. Resolve-DnsName has no HTTPS or SVCB in its record-type enum. nslookup warns once and then queries A and AAAA instead, printing an address as though it answered.
  • PowerShell itself can, with no installation. Building the query by hand and sending it over a raw UDP socket returns the record - and the ECHConfig in it matches the Linux dig result byte for byte.
  • Decoding the key shows the public name in cleartext. That is by design - it is the cover story, not a secret.

The one command that settles it

ECH depends on a key the site publishes in its DNS HTTPS record, which makes the server half of it checkable without a browser, a capture, or any trust in what a vendor claims. Asking eight names the same question produced a three-way split rather than the yes/no most write-ups imply: dnsdoh.art, crypto.cloudflare.com and defo.ie publish a key; cloudflare.com, google.com and example.com publish an HTTPS record with no key in it; github.com and wikipedia.org publish no HTTPS record at all. Four different resolvers were asked, so this is not one resolver's view.

The full walk-through, including decoding the key by hand, is in the ECH guide. It previously said large CDNs "do this already". That was too generous and has been replaced with the measurement.

Windows cannot ask the question

This was the surprise. Resolve-DnsName rejects the query outright, and helpfully prints its entire record-type enum while doing so - a list that ends at NSEC3PARAM, type 51. HTTPS is type 65 and SVCB is 64. Neither exists in the cmdlet, and the numeric form is refused the same way.

nslookup fails less visibly. Given -type=65 it prints unknown query type: 65, then sends ordinary A and AAAA queries and prints the address it gets back. The debug mode makes this unambiguous: the two SendRequest() blocks name type = A and type = AAAA, and type 65 never reaches the wire. The single warning line sits above a screenful of output, and when the same command was redirected to a file the warning did not appear in it - only the address did. Someone checking whether a site supports ECH could easily read that as an answer.

Correction: PowerShell can, after all

The guide first concluded this section with "use a browser, or install the BIND tools". That was wrong, or at least lazy, and it was shown to be wrong within the hour. The cmdlet is the limitation, not the language: about thirty lines of stock PowerShell build the 28-byte query by hand, send it over a UdpClient, and hex-dump the reply. No installation and no dependencies.

The reply is 133 bytes and every field accounts for itself: type 65, a 48-second TTL, 93 bytes of record data holding priority 1, an ALPN list of h3, h2 and http/1.1, and 67 bytes of ECH. That ECHConfig is byte for byte the one the Linux base64 -d route produced, public name included. A hand-built packet from a Windows machine and a piped one-liner on Linux agreeing to the last byte is a stronger check on both than either alone. The script is on the guide.

What we could not demonstrate

Confirming that ECH was actually used on a connection is still a browser job. curl lists an --ech option but most builds are not compiled with it, and ours reports exactly that. What the command line does show, in one line, is the problem ECH exists to solve: a tshark filter prints the server name your browser sends in plaintext on every TLS handshake, before any encryption is negotiated.