DNS over HTTPS
ActiveDNS 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.
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
| Field | Size | Description |
|---|---|---|
| GET request format | ?dns= base64url | GET /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 format | application/dns-message | POST /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-query | RFC 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 type | application/dns-message | Both request Content-Type (POST) and response Content-Type for binary DNS messages. Also Accept: application/dns-message in GET requests. |
| Caching | HTTP Cache-Control | DoH 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 DoT | RFC 7858 | DNS 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. |
| Bootstrapping | Hardcoded IP or system DNS | The 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
# 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# 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>