Technical notes by Jorge Marín

Rabbit hole 01: private DNS

| Reading time: 28 minutes
Copy permalink

Your browsing activity leaks in more places than you think. This post is about taking back one of them: DNS. And no, a private tab doesn't help — it never touched the network layer to begin with.

Today I'll write about the Domain Name System (DNS), how to self-host your own server, and the advantages, disadvantages, and privacy trade-offs of different ways of resolving Internet hosts.

Problem statement
Your browsing may not be private: third parties can observe which hosts you resolve and reach, and can decide what you can access while browsing the Internet.

What I learned
DNS, privacy, Internet censorship, DNS over HTTPS, DNS over TLS, Oblivious DNS over HTTPS, ECH
Evolution of the DNS ecosystem and related technologies. How to own your DNS infrastructure with some compromises.

Updated information as of
September 2026


Table of Contents


Gated access to the internet

As a surprise to no one, The Internet is no longer an open space. The Internet was built around a simple idea: no one should need permission from a central authority to connect, publish, or communicate.

When I invented the Web, I didn’t have to ask Vint Cerf, the ‘father of the Internet,’ for permission to use the Internet.

— Tim Berners-Lee, father of the World Wide Web.

But over time, some of the Internet’s most important doors became centralized: ISPs, DNS providers, routing infrastructure.

The network is still open. The power isn’t always distributed.

As a result of this centralization we are presented with two main issues:

This is the case with the ISPs and concepts like:

Generally, when we try to access a network resource we first query for their IP address using a domain name. The Domain Name System (DNS) gives us the IP address of the resource. Then we can talk to it.

While total privacy and decentralization is not practical (Tor network gets closer to this goal but that's a whole new rabbit hole not covered in this article), we could implement a few concepts and ideas around the domain names to increase our freedom and privacy.

Disclaimer: We can own the DNS resolver, we can encrypt our packages, we can tunnel them through a VPN. Still, the last centralized hop (authoritative DNS servers, VPN server ISP) will be able to see the activity and potentially block it.

If you only care about my final recommendations, go to Appendix TL;DR

High level DNS privacy stages

Brief DNS overview

Once upon a time, there was Internet. And so there was DNS, the Domain Name System.

DNS was designed to provide a human-readable naming system for network resources, mapping domain names such as myserver.com to resource records such as IP addresses that computers use to locate and communicate with those resources. Your device is a stub resolver. It normally asks one recursive resolver. That resolver either has the answer cached or walks the DNS hierarchy: root → TLD → authoritative server.

The following schema shows the different servers involved in a typical resolution:

This system was designed a long time ago, and its main goal was not protecting users' privacy, but making name resolution work, fast and reliably. Traditional DNS queries and responses are normally sent in plaintext, which means they can be read or modified by networks, ISPs, or anybody able to monitor the relevant transmissions. Even if a website uses HTTPS, the DNS lookup itself is a separate operation and may still be exposed.

This lack of privacy has consequences for surveillance and censorship: whoever can observe or control DNS resolution can learn something about the domains users are trying to reach and can potentially interfere with resolution.

That trade-off — a fast, reliable system that was never built with privacy in mind — is where the practical problems start.

It's always DNS

ISPs and network operators can restrict access to websites by interfering with DNS resolution. This is often done when courts or regulators order domains to be blocked, although DNS is only one of several mechanisms that can be used for Internet censorship.

Public DNS services such as Google's 8.8.8.8 and Cloudflare's 1.1.1.1 made it possible to use a resolver other than the one provided by your ISP. This removed the ISP resolver as the only DNS chokepoint, but it did not remove the concept of a chokepoint.

You would now be trusting Google, Cloudflare, Quad9, or another provider to handle your requests. The question becomes how secure and private those centralized services are. The problem exists in at least two different dimensions:

Switching from ISP DNS to a public resolver can therefore change who you trust without eliminating trust.

Public resolvers can themselves be subject to legal orders or provider policies. Google, for example, documents jurisdiction-specific blocking behavior for its Public DNS service. The same general principle has appeared in legal disputes involving other public resolvers.

The lesson is simple: switching from your ISP to 8.8.8.8 or 1.1.1.1 moves the chokepoint. It does not remove it.

All of this shows that, as individuals, we no longer control which publicly available content we can access nor who might keep a record of our browsing activity.

That chokepoint is not just an abstract idea — it maps onto specific points in how DNS resolution actually works. The next section breaks those down.

Threat model

In more precise terms, there are two DNS hops, each with a different privacy problem:

TEXT
Client ───────────────► Recursive resolver
          HOP 1

Recursive resolver ───► Authoritative DNS
          HOP 2

Hop 1: Client → Recursive

We want to protect:

DNS queries from on-path observers such as ISPs or local networks The association between our identity and our queries at the recursive resolver

DoH, DoT, and DoQ encrypt the first part. ODoH addresses the second by separating the client's identity from the DNS request.

Hop 2: Recursive → Authoritative

We want to reduce or protect:

DNS information disclosed to authoritative servers — QNAME minimization reduces unnecessary disclosure DNS traffic from passive observers — encrypted recursive-to-authoritative transport can protect it where supported DNS answer authenticity and integrity — DNSSEC protects against forged or modified DNS data

We are not trying to achieve perfect anonymity. The goal is to control which party can see which information and reduce the number of parties we need to trust.

The rest of this post walks through the technologies that address each of these hops, and how they combine into a setup you can run and own yourself.

Technologies and ideas around DNS

Evolution

In a simplified way, this is the timeline of the DNS scene as new concepts and RFCs appeared over time:

  1. DNS (the baseline)

    • Plaintext and unauthenticated, usually resolved by whatever your ISP hands you
  2. Public DNS resolvers

    • Public recursive resolvers allowed you to use an alternative to your ISP
    • Anycast deployments can provide low-latency access from many locations
    • Providers advertise different combinations of speed, availability, privacy, filtering, and logging policies
  3. DNSSEC

    • Adds cryptographic signatures so a validating resolver can verify the authenticity and integrity of DNS data
    • DNSSEC does not encrypt DNS queries or hide which names are being queried
  4. QNAME minimization

    • Limits DNS query disclosure by sending only the minimum required domain labels to each authoritative nameserver, rather than the full QNAME
  5. DNS over TLS (DoT) / DNS over HTTPS (DoH) / DNS over QUIC (DoQ)

    • Encrypt DNS traffic between a DNS client and its resolver
    • DoT uses TLS over TCP
    • DoH transports DNS inside HTTPS
    • DoQ transports DNS over QUIC
  6. Oblivious DNS over HTTPS (ODoH)

    • stub → relay → target
    • The DNS request is encrypted before reaching the relay
    • The relay knows the source IP but cannot read the DNS request
    • The target can read the DNS request but sees the relay's address instead of the client's
  7. Unilateral Opportunistic Deployment of Encrypted Recursive-to-Authoritative DNS

    • Allows recursive resolvers to opportunistically use encrypted transport when communicating with authoritative servers
    • Falls back when encrypted transport is unavailable

DNS over HTTPS / DNS over TLS

Paraphrasing Cloudflare DNS article (highly recommended):

Think of a normal, unencrypted DNS query as being like a postcard sent through the mail: anyone handling the mail may happen to catch a glimpse of the text written on the back side, so it is not wise to mail a postcard that contains sensitive or private information.

DNS over TLS and DNS over HTTPS are two standards developed for encrypting plaintext DNS traffic in order to prevent malicious parties, advertisers, ISPs, and others from being able to interpret the data. Continuing the analogy, these standards aim to put an envelope around all postcards going through the mail, so that anyone can send a postcard without worrying that someone is snooping on what they are up to.

Basically, DNS over TLS (DoT) and DNS over HTTPS (DoH) both encrypt your DNS lookups, making it harder for others to see, change, or fake the DNS traffic between your device and the resolver.

DoH and DoT therefore solve a very specific problem: they protect the path to the resolver. They do not make the resolver blind to your DNS requests.

More information: Cloudflare DNS Article

Oblivious DNS over HTTPS

Oblivious DNS over HTTPS (ODoH) takes the privacy of DoH one step further: it separates your identity from your request so that no single party has access to both pieces of information at the same time.

With normal DoH, your DNS provider can still see “this IP address asked for this website.” The connection is encrypted, but the resolver terminates that encrypted connection and therefore sees both the client IP and the DNS query.

ODoH separates those two pieces of information. Your device encrypts the DNS request for the target resolver, then sends it through a relay. The relay can see your IP address, but cannot read the DNS request. The target can read the request, but sees the relay's address instead of yours.

The relay is therefore essential: ODoH needs both an ODoH relay and an ODoH target. They are designed so that neither one alone can see both your identity and your DNS queries.

ODoH is defined in RFC 9230, published in 2022. It is an Experimental RFC rather than an Internet Standards Track specification.

ODoH only provides its split-trust property when the relay and target do not collude or share enough information to reconstruct the association. If both parties are operated by the same organization, or their logs are combined, the privacy benefit can disappear.

A global/passive observer can still potentially correlate traffic patterns even though the relay and target individually cannot see both pieces of information.

Designing a private DNS infrastructure

We start with the original DNS concept. The main problems are:

Then we introduced public DNS services offered by large companies through globally distributed Anycast networks. These can provide better performance, availability, and an alternative to ISP-controlled resolution. We still have some important trust and privacy problems:

So we add DoH/DoT/DoQ, adding encryption to the client-to-resolver path. We still have:

So we configure ODoH, separating our identity from our request so that the relay and target do not individually have the complete picture. We still have:

At this point, the three issues boil down to one common obstacle: the centralized servers.

It is tempting to think that we can simply run our own recursive resolver and remove the public recursive resolver from the equation.

And we can.

A self-hosted recursive resolver can perform iterative resolution directly against the DNS hierarchy. It does not have to forward every query to another recursive resolver. And what's more important, it can maintain a cache. On the other hand, authoritative servers will still see our recursive resolver IP as unique.

The cache is a privacy feature

DoH, DoT and ODoH protect queries in transit. A local cache means the query is not transmitted at all.

Real browsing is dominated by repeats — the same CDNs, mail hosts and API endpoints, all day. The first lookup in a TTL window goes upstream; the rest are answered from RAM in well under a millisecond, invisible on any wire.

Be honest about magnitude: popular zones use short TTLs (often 30–300 seconds on big CDNs), so the cache cuts volume, it does not eliminate it. What it reliably removes is timing metadata — the query patterns that traffic analysis of your ODoH flow would otherwise feast on.

Tip

Size the caches for real (Unbound's defaults are modest — rrset-cache-size, msg-cache-size), and don't flush recreationally.

The counter-corner: a cache is also a record. The box that holds it can reconstruct where its clients have been. You own the box — that's the point — but if it can be seized or stolen, disk encryption and access control are part of this design's threat model.

Self-hosted DNS resolver placement

Before comparing resolution strategies, let's do a quick comparison of where our DNS resolver physically runs. This is not a deployment detail, the topology determines who sits on which hop.

Option Availability ISP visibility Resolver IP privacy Roaming Main concern
Home ⚠️ Home-dependent Medium Residential IP ⚠️ VPN home required Home ISP / residential IP
VPS ✅ Always on Good ⚠️ Unique static IP ✅ Yes Datacenter singleton IP
Own VPN (VPS + VPN) ✅ Always on Strong ✅ Shared egress IP ✅ Yes VPN endpoint becomes trusted observer




Going back to the main problem, the pool of options so far (ordered in usefulness ascent mode):

  1. Self-hosted forwarder
  2. Use ODoH and separate our identity from the recursive resolver
  3. Resolve names ourselves by talking directly to root, TLD, and authoritative servers

Each option has a different trade-off.

Option #1: Self-hosted forwarder

Run a self-hosted forwarder that:

What you get over "just use 1.1.1.1":

What you don't get: the upstream still links your IP to every cache miss. No identity/query separation.

Note

One could be tempted to round-robin requests to different resolvers. Rotating upstreams is legitimate for availability and for diluting how complete any single provider's record of you is. As a privacy mechanism it's load balancing with a story attached. It distributes trust, but it does not provide cryptographic unlinkability. Correlation is still possible. No real strong anonymity.

Note

One precision: QNAME minimization happens at the recursive resolver, not at your forwarder. In forward mode your box sends full QNAMEs upstream. Hop 2's minimization is the upstream's policy, not yours. That only changes in Option #3.

Option #2: ODoH

We can accept a few trade-offs and use ODoH.

Our self-hosted server becomes a local DNS proxy/forwarder. It can send the request through an ODoH relay to a target recursive resolver while maintaining a local cache.

The relay sees our source IP but not the query. The target sees the query but sees the relay rather than our source IP.

This gives the strongest identity/query separation of the three approaches, but we now depend on the relay and target maintaining the separation.

Note

ODoH wins confidentiality (the target cannot link you); the self-hosted forwarder wins verifiability (you validate instead of trusting the target to) and latency (one hop, no relay detour). Choose based on which failure you fear more — being profiled by your resolver, or being lied to by it.

Choosing a relay: jurisdiction matters

The target sees the relay, not you. The relay’s location and legal jurisdiction therefore affect what the target can infer and what the relay provider may be required to disclose.

Tip

Important bootstrap issue: Your forwarder gets the target’s public key through a DNS HTTPS record. If that DNS lookup is unencrypted, the supposedly private connection starts with a plaintext lookup. Protect the initial DNS lookup with encryption or pin the public key.

Choose relays based not just on network location, but also on jurisdiction, provider separation, network concentration, and secure key bootstrapping.

Option #3: recursive resolution ourselves

We can configure our server to resolve domain names directly by walking the DNS hierarchy.

Walking root → TLD → authoritative yourself removes the recursive operator from your trust chain. The earlier framing — that authoritative servers are "not consumer resolvers whose job is maintaining a user's query history" — deserves a correction, because it is naive:

Mitigations if you still want full recursion: run it behind a shared-egress VPN (restores the crowd), QNAME minimization (limits per-server disclosure), aggressive caching (fewer queries emitted at all).

Full recursion maximizes ownership and authenticity, but measured against the authoritative tier it is a privacy step backward unless your egress IP is shared. It also introduces another problem: the recursive-to-authoritative path.

After our resolver has the question, it still has to walk:

root → TLD → authoritative

That walk has traditionally used ordinary DNS transport. Plaintext recursion still exposes names to the network in front of your resolver. DoH, DoT, and ODoH were primarily designed for the first hop, from the client to a recursive resolver.

ODoH cannot simply be aimed at a root server: an ODoH target is a recursive DNS service, while root and TLD servers are authoritative servers.

A normal NS record gives us names and addresses for authoritative servers, not a DoH URL. A recursive resolver therefore cannot simply assume that the other side speaks HTTPS.

The recursive-to-authoritative hop has its own mechanisms, including DoT and DoQ. Most authoritative servers, however, still support traditional DNS on port 53, and a resolver cannot require encrypted transport from every authoritative server without risking failed resolutions.

This is where RFC 9539, Unilateral Opportunistic Deployment of Encrypted Recursive-to-Authoritative DNS, comes in.

RFC 9539 is the pragmatic workaround: a resolver can probe for encrypted DNS transport and use it when the authoritative server supports it, while retaining the ability to use ordinary DNS when it does not.

But that's the catch.

Opportunistic encryption does not mean encryption is guaranteed.

An active attacker can interfere with the encrypted connection attempt and cause the resolver to fall back to ordinary DNS. RFC 9539 primarily improves protection against passive monitoring; it does not provide robust protection against active attackers.

The resolver also cannot assume that every authoritative server on the Internet has upgraded. The RFC is designed around unilateral adoption: operators can deploy it without requiring the entire DNS ecosystem to coordinate first.

A few root-server operators have experimented with encrypted transport, but there is still no guarantee that a complete resolution path from root through TLD to authoritative server will be encrypted.

The point of RFC 9539 is therefore not “DNS is now encrypted end to end.” It is a phased rollout that allows encryption where possible without breaking resolution where it is not.

That leaves a real trade-off — recurse ourselves and accept both the plaintext fallback and singleton exposure, or forward through ODoH and let a big public target run hop 2 inside its crowd. The setup below does the second, which also sidesteps the singleton problem entirely: with ODoH forwarding, authoritative servers see the target's anycast mass, not your address.

Final setup and future

We need:

We still need both a relay and a target recursive service that we trust to provide correct answers and respect the intended privacy separation. That is the compromise.

My final setup looks like this:

TEXT
      Client
        │
        │ VPN (all traffic, including DNS) or DoH
        │
        ▼
 Self-hosted DNS
 proxy / forwarder
        │
        │ ODoH
        │
        ▼
      Relay
        │
        │ ODoH
        │
        ▼
 Global Public DNS
 Recursive Resolver
              │
              │ DNS query (mostly plaintext DNS, maybe RFC 9539)
              │
              ├── 1. Root ──────► "Which TLD?"
              │                    │
              │◄───────────────────┘
              │
              ├── 2. TLD ───────► "Which authoritative server?"
              │                    │
              │◄───────────────────┘
              │
              └── 3. Authoritative
                       ───────────► "Here is the final IP"
                       ◄───────────┘

The self-hosted component is not doing the recursive walk. It provides local control, the cache, and the choice of relay and target — and both of those can be changed the day either earns distrust.

One visible improvement not included in this solution is a VPN egress (shared IP) so that the relay cannot uniquely identify us. Although it won't be able to see the request content so I am ok with that.

Worth noticing, this is not all for free. ODoH costs you increased latency on cache misses.

Who can see what?

The goal of the final architecture is not to make every component blind, but to split the information between them.

Participant Our IP DNS query
Local network / ISP VPN endpoint only No
VPN provider Yes (terminates the tunnel) No — only encrypted flows toward the relay
Self-hosted forwarder Yes Yes — and it holds the cache, which is a local history
ODoH relay Shared VPN egress IP No
ODoH target No — sees the relay Yes
Authoritative DNS No — sees the target's crowd The names the target asks on our behalf
Hosting provider of the box Encrypted flows only No
Destination servers Your VPN egress IP, plus your TLS fingerprint — they see connections, not lookups

The key ODoH property is:

TEXT
Relay       → knows who is asking, but not what they ask
Target      → knows what is being asked, but not who originally asked

The self-hosted proxy is not part of this anonymity boundary: it can see both the client and the DNS request. Its purpose is to provide local control and forward the request through ODoH.

This separation depends on the relay and target not colluding or combining their information.

Bonus - ECH and RFC 9849

While researching ISP censorship, I came across another technique networks can use to restrict access to websites. It's called SNI / domain-based blocking.

Encrypted DNS only hides the lookup. The browser then opens a TLS connection to an IP address. Without ECH, the TLS ClientHello can expose the hostname through SNI.

A network that never saw the DNS query can therefore still learn the hostname and potentially block it.

ECH, defined in RFC 9849, encrypts the relevant TLS ClientHello information so that the hostname is no longer exposed to an on-path observer in the same way.

ECH does not hide the destination IP, does not replace DNS encryption, and requires support from both the client and the server.

The practical stack is therefore layered:

None of these mechanisms alone provides complete anonymity. They solve different parts of the problem.

Appendix 1 - TL;DR

For maximum privacy and ownership

For strong privacy and some ownership

For not-so-strong privacy and full ownership

For convenience:




Note

Point your OS/browser/VPN to it:

  • Configure encrypted DNS at the layer that actually performs DNS resolution.
    • If your operating system supports system-wide DoH or DoT, configure it there.
    • Alternatively, configure a browser's DoH settings (better if it supports ODoH)
  • (optionally) Configure your VPN server to use those specific DNS servers See technical section on this post for steps on your operating system and browser.

Tip

Some open source, self-hosted options for DNS recursive resolvers:

Not having encrypted DNS at the operating-system level means applications may use whatever DNS configuration is available to them, which might be plaintext. Browser DoH, a local stub (stubby, dnscrypt-proxy, Numa), or a VPN DNS setting can still encrypt.

Not having DoH configured in your browser means every webpage you visit with it will hit the DNS set by default, usually inherited from the operating system's resolver configuration, which might be plaintext.

Not having server ownership means your DNS requests will go through someone else's service. That service may or may not be private, may or may not log activity, and may be subject to filtering, legal orders, or other policies.

Appendix 2 - DoT and DoH implementation

Use system-wide encrypted DNS where your operating system supports it; use browser settings if it cannot inherit secure/private configuration from your operating system.

The exact menus vary by OS version.

Platform Protocol High-level setup
macOS 13+ DoT Install/create a DNS Settings configuration profile with DNSProtocol = TLS, your resolver IP(s), and its TLS hostname. macOS supports DoT natively.
Linux DoT Use systemd-resolved if available: configure a resolver with DNS= and DNSOverTLS=yes, then restart/enable systemd-resolved. Alternatively use stubby or dnscrypt-proxy.
Windows 11 DoT Don't use Windows' built-in encrypted DNS for DoT; current Windows settings are for DoH. For DoT, use a local resolver such as dnxcrypt-proxy, stubby, or another DoT-capable client, then point Windows DNS at 127.0.0.1.
iOS 16+ DoT Install a DNS configuration profile/app that uses Apple's DNS Settings framework with DNSProtocol = TLS, server IP(s), and ServerName. iOS supports system-wide DoT.
Android 9+ DoT Settings → Network & Internet → Private DNS → Private DNS provider hostname → enter your resolver's DoT hostname → Save.
Firefox DoH Settings → Privacy & Security → DNS over HTTPS → Advanced settings → choose Custom or Max Protection → select/enter your DoH provider.
Chrome DoH Settings → Privacy and security → Security → Use secure DNS → enable → choose current provider or custom provider.

System-level and browser-level encrypted DNS are not mutually exclusive. If a browser is configured to use its own DoH service, it may bypass the operating system's DNS configuration for its own queries.

If you want strict encryption, disable plaintext fallback where the relevant implementation offers that option. Be aware that some automatic encrypted-DNS modes intentionally fall back to ordinary DNS when the secure resolver cannot be reached.

Some examples

For Cloudflare:

TEXT
DoT hostname: cloudflare-dns.com / one.one.one.one / 1dot1dot1dot1.cloudflare-dns.com

IPv4:          1.1.1.1
               1.0.0.1

DoH URL:       https://cloudflare-dns.com/dns-query

For Quad9:

TEXT
DoT hostname: dns.quad9.net

IPv4:          9.9.9.9
               149.112.112.112

DoH URL:       https://dns.quad9.net/dns-query

Important distinction

Resources


UPDATE 1 (05/09/2026) Mullvad DNS shutdown