
What is DNS over HTTPS (DoH)?
DNS over HTTPS (DoH) is a protocol that sends DNS queries and responses inside ordinary encrypted HTTPS connections, instead of as plain-text packets on UDP or TCP port 53. Your device or browser sends each DNS question to a DoH server as an HTTP request, usually over port 443, and gets the answer back in the HTTP response. Because the traffic is encrypted with TLS and authenticated with the server's certificate, people on the network path, such as other users on public Wi-Fi or an intermediate network, can't read which names you look up or tamper with the answers. DoH is standardised in RFC 8484 and is supported by all major browsers, by Windows, macOS, iOS and Android, and by the large public resolvers.
Classic DNS was designed without any privacy or integrity protection, so every lookup reveals the sites and services you use to anyone who can see the traffic. DoH closes that gap for the hop between your device and your resolver. In this article you'll learn how DoH works at the protocol level, how to send DoH queries yourself with curl, dig and Python, how to turn it on in browsers and operating systems, how to run your own DoH server, and what it means for network administrators who rely on DNS for filtering and monitoring.
The Problem DoH Solves
When your device looks up a name over traditional DNS, the query travels unencrypted to a recursive resolver. Anyone who can see that traffic can:
- See every name you look up, which reveals the websites, apps and services you use, even if the connections to those sites are encrypted with HTTPS.
- Modify the answers, sending you to a different server or blocking a site by returning a fake "doesn't exist" response.
- Redirect your queries to a different resolver without you knowing, which some networks do by intercepting all traffic on port 53.
DNSSEC can protect the integrity of answers for signed zones, but it doesn't hide what you're looking up. DoH protects both privacy and integrity for the connection between your device and the resolver.
How DoH Works
DoH carries normal DNS messages, in the standard binary wire format, inside HTTP requests. Nothing about the DNS message itself changes, only how it's transported.
- The client opens an HTTPS connection to the DoH server, for example
https://cloudflare-dns.com/dns-query, and verifies the server's TLS certificate. - It encodes the DNS query in wire format and sends it in an HTTP request.
- The server resolves the query like any recursive resolver would.
- It returns the DNS response in wire format in the body of the HTTP response.
The connection stays open, so later queries reuse it without a new TLS handshake. With HTTP/2 or HTTP/3, many queries can be in flight at once on a single connection.
GET and POST Requests
RFC 8484 defines two ways to send a query.
GET puts the wire-format query, encoded as base64url without padding, in a dns query parameter:
GET /dns-query?dns=AAABAAABAAAAAAAAA3d3dwdleGFtcGxlA2NvbQAAAQAB HTTP/2
Host: cloudflare-dns.com
Accept: application/dns-message
That string encodes a query for the A record of www.example.com. RFC 8484 recommends using a query ID of 0 with DoH, because a fixed ID makes identical GET requests cacheable by HTTP caches.
POST sends the raw wire-format query as the request body, with the Content-Type set to application/dns-message:
POST /dns-query HTTP/2
Host: cloudflare-dns.com
Content-Type: application/dns-message
Accept: application/dns-message
Content-Length: 33
<33 bytes of binary DNS query>
Both return the response with the media type application/dns-message, and caching is controlled with standard HTTP headers based on the TTLs in the answer.
JSON APIs
Some providers also offer a JSON interface that's handy for scripts and debugging. It isn't part of RFC 8484, so the exact format varies by provider, but Google and Cloudflare both support one.
Trying DoH Yourself
With curl and the JSON API
curl -s -H 'accept: application/dns-json' \
'https://cloudflare-dns.com/dns-query?name=example.com&type=A' | jq
{
"Status": 0,
"TC": false,
"RD": true,
"RA": true,
"AD": true,
"CD": false,
"Question": [
{ "name": "example.com", "type": 1 }
],
"Answer": [
{ "name": "example.com", "type": 1, "TTL": 1726, "data": "192.0.2.10" }
]
}
Status: 0 means NOERROR, and AD: true means the resolver validated the answer with DNSSEC. Google's equivalent is at https://dns.google/resolve?name=example.com&type=A.
With curl and the Wire Format
You can send an RFC 8484 GET request directly, using the encoded query from earlier, and inspect the binary response:
curl -s -H 'accept: application/dns-message' \
'https://cloudflare-dns.com/dns-query?dns=AAABAAABAAAAAAAAA3d3dwdleGFtcGxlA2NvbQAAAQAB' |
xxd | head -n 4
The output is a raw DNS message, the same bytes a resolver would send over UDP. It's not very readable, which is why tools like dig are more convenient.
With curl as a DoH Client
curl can also use DoH for its own name resolution, so the lookup of the site you're fetching goes over HTTPS:
curl --doh-url https://dns.google/dns-query -sI https://www.example.com/ | head -n 1
This is a quick way to check whether DoH resolution works from a particular network.
With dig and kdig
BIND 9.18 and later include DoH support in dig with the +https option:
dig @1.1.1.1 +https www.example.com A
;; ANSWER SECTION:
www.example.com. 300 IN A 192.0.2.80
;; Query time: 18 msec
;; SERVER: 1.1.1.1#443(1.1.1.1) (HTTPS)
The SERVER line shows port 443 and the HTTPS transport. Knot's kdig offers the same:
kdig @1.1.1.1 +https www.example.com A
With Python
dnspython supports DoH through dns.query.https, which needs the httpx package:
#!/usr/bin/env python3
"""Look up a name over DNS over HTTPS."""
import sys
import dns.message
import dns.query
import dns.rdatatype
name = sys.argv[1] if len(sys.argv) > 1 else "www.example.com"
query = dns.message.make_query(name, dns.rdatatype.A)
query.id = 0 # RFC 8484 recommends ID 0 for cacheability
response = dns.query.https(query, "https://cloudflare-dns.com/dns-query", timeout=5)
for rrset in response.answer:
print(rrset.to_text())
pip install "dnspython[doh]"
python3 doh_lookup.py www.example.com
www.example.com. 300 IN A 192.0.2.80
Enabling DoH on Your Devices
Browsers
- Firefox: Go to Settings > Privacy & Security > DNS over HTTPS, and choose Default, Increased or Max Protection, along with a provider. In some regions, Firefox enables DoH by default.
- Chrome and Edge: Go to Settings > Privacy and security > Security > Use secure DNS. By default, Chrome upgrades to DoH automatically if your current resolver is a known provider that supports it, and you can also choose a specific provider or enter a custom DoH URL.
- Safari: Uses the operating system's DNS settings, including any encrypted DNS configured through a profile.
Windows 11
Windows 11 supports DoH natively. In Settings > Network & internet, open your connection's properties, edit the DNS server assignment, enter a resolver that Windows recognises as supporting DoH, and set DNS over HTTPS to On. You can see the resolvers Windows knows about in PowerShell:
Get-DnsClientDohServerAddress
To add your own DoH resolver to that list:
Add-DnsClientDohServerAddress -ServerAddress 192.0.2.53 `
-DohTemplate 'https://dns.example.com/dns-query' `
-AllowFallbackToUdp $false -AutoUpgrade $true
Setting -AllowFallbackToUdp $false stops Windows falling back to plain DNS if the DoH server is unreachable, which is more private but means lookups fail outright in that case.
macOS and iOS
Apple platforms support encrypted DNS through configuration profiles that use the DNS Settings payload, or through apps that use the system's network extension APIs. Public resolver providers publish ready-made profiles, and organisations can deploy them through mobile device management.
Android
Android's Private DNS setting uses DNS over TLS rather than DoH, although some apps and browsers on Android use DoH independently.
Running Your Own DoH Server
If you'd rather not send your queries to a public provider, you can serve DoH from your own resolver. Recent versions of Unbound can serve DoH directly:
server:
interface: 0.0.0.0@443
interface: ::0@443
https-port: 443
http-endpoint: "/dns-query"
tls-service-key: "/etc/unbound/tls/privkey.pem"
tls-service-pem: "/etc/unbound/tls/fullchain.pem"
access-control: 192.0.2.0/24 allow
access-control: 2001:db8:100::/48 allow
Unbound must be built with libnghttp2 for DoH support. Check with:
unbound -V | grep -i nghttp2
Use a certificate for a hostname such as dns.example.com from a public certificate authority, so clients can verify it, and restrict access-control to the networks you serve. Running an open DoH resolver on the internet carries the same abuse risks as any open resolver. Other options include BIND 9.18 and later, which can serve DoH natively, or a DoH proxy in front of an existing resolver.
Test your server from a client:
dig @dns.example.com +https www.example.com A
Discovering DoH Automatically
Configuring a DoH URL by hand isn't practical on every network, so newer standards help clients find encrypted resolvers automatically:
- DDR (Discovery of Designated Resolvers, RFC 9462) lets a client ask its current resolver, using a special
_dns.resolver.arpaquery for an SVCB record, whether it also offers DoH or another encrypted protocol. - DNR (Discovery of Network-designated Resolvers, RFC 9463) lets networks advertise encrypted resolvers through DHCP and IPv6 router advertisements.
You can see whether a resolver advertises DDR:
dig @1.1.1.1 _dns.resolver.arpa SVCB +short
1 one.one.one.one. alpn="h2,h3" port=443 ipv4hint=1.1.1.1,1.0.0.1 ipv6hint=2606:4700:4700::1111,2606:4700:4700::1001 key7="/dns-query{?dns}"
2 one.one.one.one. alpn="dot" port=853 ipv4hint=1.1.1.1,1.0.0.1 ipv6hint=2606:4700:4700::1111,2606:4700:4700::1001
The first record advertises DoH, with the dohpath (shown here as key7 by some versions of dig) giving the URL template. Windows and Apple platforms use DDR to upgrade to encrypted DNS when the resolver supports it.
DoH on Managed Networks
DoH is great for privacy on untrusted networks, but it can get in the way on networks that rely on DNS for security. If a browser sends queries straight to a public DoH provider, they bypass the organisation's resolver, its filtering, and its logs.
There are well-established ways to manage this:
- Use policies: Chrome, Edge and Firefox all support enterprise policies to disable DoH or point it at your own resolver. For Chrome, the
DnsOverHttpsModepolicy controls this, and Firefox uses theDNSOverHTTPSpolicy. - Offer your own DoH: Run DoH on your internal resolver and push its URL to devices, so you keep encryption and visibility.
- Use the Firefox canary domain: Firefox checks
use-application-dns.net. If your resolver returnsNXDOMAINfor that name, Firefox doesn't enable DoH by default on that network. It doesn't override a user's explicit choice. - Block known public DoH endpoints at the firewall, with care. DoH runs on port 443 and looks like other HTTPS traffic, so blocking relies on lists of known DoH servers and will never be complete.
In Unbound, you can answer the canary domain with NXDOMAIN like this:
server:
local-zone: "use-application-dns.net." always_nxdomain
Check that it's working:
dig use-application-dns.net A | grep status
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 51884
What DoH Doesn't Do
It's worth being clear about DoH's limits:
- It only protects the hop to your resolver. The resolver itself sees every query, so you're trusting whichever DoH provider you choose. The resolver's own lookups to authoritative servers are usually still unencrypted.
- It doesn't hide the sites you visit entirely. After the lookup, your device connects to the site's IP address, and unless Encrypted Client Hello is in use, the site's name is still visible in the TLS handshake.
- It doesn't authenticate the DNS data itself. DoH proves you're talking to the right resolver, but only DNSSEC proves the records came from the domain owner.
DNS over TLS (DoT) protects the same hop with a dedicated port, 853, rather than blending in with web traffic on port 443. Both are valid choices, and many resolvers support both.
FAQ: DNS over HTTPS
Yes, for the connection between your device and the resolver. DoH encrypts your queries so others on the network can't read or alter them. You still need to trust the DoH provider, and DNSSEC is still needed to verify the records themselves.
Usually not noticeably. The first connection needs a TLS handshake, but it's reused for later queries, and a well-run DoH provider close to you can be as fast as, or faster than, an ISP resolver.
Your ISP can no longer see your DNS queries, but it can still see the IP addresses you connect to. Without Encrypted Client Hello, it may also see site names in the TLS handshake.
DoH normally uses TCP port 443, the same as regular HTTPS, or UDP port 443 when HTTP/3 is used. That makes it hard to distinguish from other web traffic.
Several public resolvers offer a test page that shows whether your browser is using their DoH service, such as Cloudflare's test at one.one.one.one/help. In Firefox, the about:networking page also shows DNS details.
They can manage it through browser and operating system policies, answer the Firefox canary domain with NXDOMAIN, and block known DoH endpoints. The most effective approach is usually to offer an internal DoH resolver and enforce it by policy.
It makes sense if you want encrypted DNS without trusting a third party, or if you manage a network and want to keep filtering and logging. Restrict access to your own clients so you don't create an open resolver.
Conclusion
DNS over HTTPS wraps ordinary DNS messages in encrypted HTTPS connections, so the names you look up are hidden from the network and the answers can't be tampered with on the way to your device. It's built on the same HTTP and TLS technology as the rest of the web, which is why browsers and operating systems have been able to adopt it so widely, and why it's easy to try with curl, dig +https or a few lines of Python.
Turn it on for devices that use untrusted networks, choose a resolver you trust, and remember that it protects the hop to the resolver rather than the whole journey. If you manage a network, embrace it rather than fight it: run your own DoH resolver, publish it to your devices through policy, and you keep both privacy for users and the visibility your security tools rely on.


