Skip to main content
DoH

DNS over HTTPS

Active

DNS over HTTPS (DoH) is an IETF protocol (RFC 8484) that encrypts DNS queries by sending them as HTTPS requests rather than plaintext UDP packets. Standard DNS (port 53) is unencrypted – any network observer can see every domain a user resolves. DoH wraps DNS queries in HTTPS, making them indistinguishable from regular web traffic. DoH is enabled by default in Firefox and Chrome and is deployed by Cloudflare (1.1.1.1), Google (8.8.8.8), and NextDNS.

DoHDNSRFC 8484PrivacyHTTPS2018

In one line

DNS over HTTPS (RFC 8484, October 2018) encrypts DNS queries inside HTTPS requests. Instead of sending DNS messages as plaintext UDP on port 53, DoH sends them as HTTP POST or GET requests with Content-Type: application/dns-message to a DoH resolver URL. DoH is indistinguishable from regular HTTPS traffic – no port blocking, no eavesdropping on queries. Firefox, Chrome, Edge, and Android 9+ support DoH. The tradeoff: DNS privacy moves from your ISP's resolver to your DoH provider (Cloudflare, Google, or self-hosted).

Quick Reference

FieldSizeDescription
GET request format?dns= base64urlGET /dns-query?dns=<base64url(DNS_message)> HTTP/1.1. DNS message is the same binary format as UDP DNS (RFC 1035) but base64url-encoded without padding. Accept: application/dns-message.
POST request formatapplication/dns-messagePOST /dns-query HTTP/1.1. Content-Type: application/dns-message. Body: raw binary DNS message (same format as UDP DNS). POST is preferred for large queries and privacy (no query string in URL logs).
Well-known URI/.well-known/dns-queryRFC 8484 §8.1 registers /.well-known/dns-query as the default DoH endpoint path. Resolvers may use custom paths (Cloudflare uses /dns-query, Google uses /dns-query or /resolve for JSON API).
MIME typeapplication/dns-messageBoth request Content-Type (POST) and response Content-Type for binary DNS messages. Also Accept: application/dns-message in GET requests.
CachingHTTP Cache-ControlDoH responses use standard HTTP caching. Cache-Control: max-age=N matches the DNS TTL. GET requests are cacheable by default; POST is not. Use GET for cacheable queries.
vs DoTRFC 7858DNS over TLS (DoT, RFC 7858) uses a dedicated TLS connection on port 853. DoH uses HTTPS on port 443. DoH is harder to block (indistinguishable from web traffic), easier to deploy (no new port). DoT is easier to monitor/filter in enterprise environments.
BootstrappingHardcoded IP or system DNSThe DoH resolver itself is resolved by URL. To avoid a chicken-and-egg problem, implementations hardcode the IP address of the DoH resolver or use the system resolver for the first lookup.

Key Characteristics

Traffic privacy

DoH hides DNS queries from local network observers: ISPs, corporate network monitoring, and anyone on the same Wi-Fi. Queries are encrypted inside TLS and look identical to regular HTTPS traffic.

Centralization concern

DoH concentrates DNS resolution at a few large providers (Cloudflare, Google). A single provider outage or policy change affects millions of users. Enterprise environments often block DoH to maintain DNS-based filtering and monitoring.

HTTP/2 multiplexing

DoH over HTTP/2 multiplexes multiple DNS queries over a single TLS connection, avoiding per-query connection overhead. Firefox's DoH implementation uses HTTP/2 connection pooling for near-native DNS performance.

Split-horizon DNS challenge

Enterprise split-horizon DNS (different answers for internal vs external networks) breaks with DoH if the DoH provider doesn't know about internal zones. Enterprises must either disable DoH, use a self-hosted DoH resolver, or configure DoH exclusions for internal domains.

Message Format

Request
http
# DoH GET request (base64url-encoded DNS query for example.com A record)
GET /dns-query?dns=AAABAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE HTTP/1.1
Host: 1.1.1.1
Accept: application/dns-message

# DoH POST request (preferred for privacy – no URL logging)
POST /dns-query HTTP/1.1
Host: 1.1.1.1
Content-Type: application/dns-message
Content-Length: 29
Accept: application/dns-message
[binary DNS message: same format as UDP DNS RFC 1035]

# Cloudflare DoH (JSON API variant – not RFC 8484 but widely used)
GET /dns-query?name=example.com&type=A HTTP/1.1
Host: 1.1.1.1
Accept: application/dns-json
Response
http
# DoH binary response
HTTP/1.1 200 OK
Content-Type: application/dns-message
Cache-Control: max-age=3600
[binary DNS response: same format as UDP DNS RFC 1035]

# Cloudflare JSON API response
HTTP/1.1 200 OK
Content-Type: application/dns-json

{
  "Status": 0,           // 0 = NOERROR
  "TC": false,           // truncated
  "RD": true,            // recursion desired
  "RA": true,            // recursion available
  "AD": true,            // authenticated data (DNSSEC)
  "CD": false,           // checking disabled
  "Question": [{ "name": "example.com.", "type": 1 }],
  "Answer": [
    { "name": "example.com.", "type": 1, "TTL": 3600, "data": "93.184.216.34" }
  ]
}

# Popular DoH resolvers
# Cloudflare:  https://1.1.1.1/dns-query
# Google:      https://8.8.8.8/dns-query
# Quad9:       https://9.9.9.9/dns-query
# NextDNS:     https://dns.nextdns.io/<config-id>

Implementations

linuxsince systemd-resolved (DoH support), dnscrypt-proxy, cloudflared (Cloudflare's DoH daemon), curl --doh-urlavailable
macossince Safari uses iCloud Private Relay (ODoH). cloudflared CLI. Firefox/Chrome DoH built-in.available
windowssince Windows 11 native DoH in Settings > DNS. Chrome/Firefox/Edge DoH built-in.built-in
iossince iCloud Private Relay uses ODoH (iOS 15+). Firefox iOS DoH. DNS configuration profiles (DoH via MDM).built-in
androidsince Android 9+ Private DNS setting supports DoT by default. Chrome Android supports DoH. Firefox Android DoH.built-in