Your ISP sees every domain you look up. So does the operator of the café Wi-Fi, your mobile carrier, and any network appliance on the path between your machine and the recursive resolver. Plain DNS — still the default on most devices in 2026 — is unauthenticated UDP on port 53, fully readable and injectable by anyone on that path. dnscrypt-proxy is the production fix: a lightweight local proxy that encrypts and authenticates your DNS traffic using DNSCrypt v2, DoH, or Oblivious DoH, with DNSSEC validation, caching, and blocklists built in. This guide covers a complete setup on Windows, macOS, and Linux, plus the verification steps and 2026-specific considerations most other tutorials skip entirely.

What dnscrypt-proxy Does Under the Hood

When dnscrypt-proxy starts, it binds to 127.0.0.1:53 and becomes your machine's local resolver. Your OS sends DNS queries to it. The proxy encrypts them, authenticates the remote resolver using its published public key, and forwards them over DNSCrypt, DoH, or ODoH. Responses are verified on the way back before being returned to your application. No party between your machine and the upstream resolver — your ISP, router, or a corporate inspection appliance — can read, modify, or inject responses.

This is fundamentally different from simply switching to 1.1.1.1 or 8.8.8.8. Those use plain UDP on port 53 by default and are completely unprotected in transit. Even using a resolver that supports DNS-over-HTTPS requires dedicated client software to actually encrypt the query from your machine — which is exactly what dnscrypt-proxy provides.

Before You Begin

  • Admin (Windows/macOS) or root (Linux) access on the target machine.
  • Port 53 on localhost must be free. Check it: ss -tlnp | grep :53 on Linux, netstat -ano | findstr :53 on Windows, lsof -i :53 on macOS.
  • A resolver in mind, or use auto-selection. The public resolvers list on GitHub has 300+ options tagged by capability: DNSSEC, no-log, no-filter, IPv6, ODoH.
💡 Run dnscrypt-proxy -list-all after install to benchmark every eligible resolver against your actual network. Under 30ms round-trip is solid. Over 100ms will be noticeable.

Installing on Windows

The official pre-built binary runs as a Windows service — there is no GUI installer.

  1. Download the latest dnscrypt-proxy-win64-*.zip from the GitHub releases page for the project.
  2. Extract to C:\dnscrypt-proxy\ — keep the path short with no spaces.
  3. In the extracted folder, rename example-dnscrypt-proxy.toml to dnscrypt-proxy.toml.
  4. Edit the toml (see Configuration section below), then open PowerShell as Administrator:
cd C:\dnscrypt-proxy\ .\dnscrypt-proxy.exe -service install .\dnscrypt-proxy.exe -service start .\dnscrypt-proxy.exe -service status

Point your adapter's DNS to the local proxy: Control Panel → Network and Internet → Network Connections, right-click your active adapter → Properties → Internet Protocol Version 4 → Use the following DNS server addresses → enter 127.0.0.1. In the IPv6 Properties tab, set the DNS server to ::1. Remove any secondary DNS entries — a fallback to an unencrypted server defeats the purpose entirely.

To uninstall cleanly later: run .\dnscrypt-proxy.exe -service stop then .\dnscrypt-proxy.exe -service uninstall.

Installing on macOS

Homebrew handles the binary, config scaffolding, and launchd integration in one step:

brew install dnscrypt-proxy # Config locations: # Intel Mac: /usr/local/etc/dnscrypt-proxy.toml # Apple Silicon: /opt/homebrew/etc/dnscrypt-proxy.toml

Edit your toml (see below), then start the service:

sudo brew services start dnscrypt-proxy

Set the system DNS in System Settings → Network, select your active interface → Details → DNS tab → click + and add 127.0.0.1. Remove all other entries. On Apple Silicon Macs running macOS Sequoia or later, mDNSResponder occasionally reclaims port 53 after sleep cycles. The workaround is binding dnscrypt-proxy to port 5300 in the toml and using a pf redirect rule — details in the Troubleshooting section below.

Installing on Linux

Most modern Linux distributions run systemd-resolved on 127.0.0.53:53, which will conflict. Disable it first:

sudo systemctl stop systemd-resolved sudo systemctl disable systemd-resolved

Break the resolv.conf symlink and write a static file:

sudo rm /etc/resolv.conf echo "nameserver 127.0.0.1" | sudo tee /etc/resolv.conf sudo chattr +i /etc/resolv.conf

The chattr +i flag makes the file immutable so DHCP clients cannot silently overwrite it. Now install dnscrypt-proxy:

# Debian / Ubuntu sudo apt install dnscrypt-proxy # Arch Linux sudo pacman -S dnscrypt-proxy # Fedora / RHEL sudo dnf install dnscrypt-proxy # Manual (any distro, any architecture) wget https://github.com/DNSCrypt/dnscrypt-proxy/releases/latest/download/dnscrypt-proxy-linux_x86_64-2.1.x.tar.gz tar xzf dnscrypt-proxy-linux_x86_64-*.tar.gz sudo mv linux-x86_64/dnscrypt-proxy /usr/local/sbin/ sudo mkdir /etc/dnscrypt-proxy sudo cp linux-x86_64/example-dnscrypt-proxy.toml /etc/dnscrypt-proxy/dnscrypt-proxy.toml

Enable and start the service:

sudo systemctl enable dnscrypt-proxy sudo systemctl start dnscrypt-proxy sudo systemctl status dnscrypt-proxy

Configuring dnscrypt-proxy.toml

The toml file is where most of the power lives. The defaults are safe but conservative. Here is what to actually change for a hardened 2026 setup.

Resolver Selection

Leave server_names empty to use automatic latency-based selection, or pin specific servers by name:

server_names = ['cloudflare', 'quad9-dnscrypt-ip4-filter-pri', 'nextdns']

The sources block fetches the live stamp list from the official DNSCrypt resolvers repo on GitHub. Leave the default URL unchanged unless you are running an air-gapped setup.

Security and Privacy Flags

# Require resolvers that validate DNSSEC require_dnssec = true # Only connect to resolvers claiming a no-logging policy require_nolog = true # Set true to exclude resolvers that apply any content filtering require_nofilter = false # Refuse answers with bogus IP addresses (RFC 8020 compliance) bogus_nxdomain = []

Caching

cache = true cache_size = 4096 cache_min_ttl = 2400 cache_max_ttl = 86400 cache_neg_min_ttl = 60 cache_neg_max_ttl = 600

IPv6 in 2026

With IPv6 accounting for over 45% of global traffic, binding only to 127.0.0.1 leaves AAAA queries unprotected on dual-stack systems. Always include the IPv6 loopback:

listen_addresses = ['127.0.0.1:53', '[::1]:53']

Also verify your chosen resolver supports IPv6 — the public resolvers list tags entries with ipv6 when they do.

Built-in Blocklists

dnscrypt-proxy 2.1+ can block domains locally without a separate Pi-hole:

[blocked_names] blocked_names_file = '/etc/dnscrypt-proxy/blocked-names.txt' log_file = '/var/log/dnscrypt-proxy/blocked.log' log_format = 'tsv'

Point it at any hosts-format or one-domain-per-line blocklist. Run dnscrypt-proxy -resolve badsite.example to test if a domain is being blocked before deploying to production.

DNSCrypt vs DoH vs DoT vs ODoH

dnscrypt-proxy supports four protocols. The right choice depends on your threat model and network environment:

  • DNSCrypt v2 — purpose-built for DNS, minimal overhead, resolver authenticated via public key embedded in its stamp. Best choice when the resolver supports it.
  • DNS-over-HTTPS (DoH) — runs over HTTPS on port 443, indistinguishable from web traffic. Survives DNS-blocking firewalls. Slightly higher latency than DNSCrypt due to TLS handshake overhead.
  • DNS-over-TLS (DoT) — dedicated port 853. Easy to identify and block on enterprise or hotel firewalls. Fine for home networks where you control the router.
  • Oblivious DoH (ODoH) — 2025/2026 addition: your query routes through a relay that strips your IP before forwarding to the resolver. The relay sees your IP but not the query content; the resolver sees the query content but not your IP. Cloudflare, Fastly, and several others now run ODoH infrastructure. Look for entries tagged odoh in the public resolvers list.
💡 After switching resolvers, use our DNS Propagation Checker to confirm the new upstream's responses match authoritative records — a quick sanity check when switching from a filtering to a non-filtering resolver.

Verifying Encryption Is Actually Active

Starting the service and pointing your OS at 127.0.0.1 is not sufficient on its own — verify that queries are encrypted and the correct upstream is answering:

# Built-in test — shows protocol, resolver, and DNSSEC status dnscrypt-proxy -resolve example.com # Query via dig against the local proxy dig @127.0.0.1 example.com # Windows — nslookup nslookup example.com 127.0.0.1 # Linux — resolvectl (if systemd-resolved is still partially active) resolvectl query example.com

The -resolve output shows the upstream server name, protocol (dnscrypt / doh / odoh), DNSSEC validation result, and round-trip time. A healthy response looks like:

Resolver : cloudflare Protocol : DNSCrypt DNSSEC : yes RTT : 14ms

Run a DNS leak test from the target machine at dnsleaktest.com or browserleaks.com/dns. The reported resolvers should match your chosen upstream, not your ISP. For per-record verification, the DNS Lookup tool on this site lets you compare external authoritative results against what dnscrypt-proxy resolves locally — useful for catching split-horizon mismatches.

Keeping It Running After Reboots and Network Changes

The service itself persists — systemd on Linux, launchd via Homebrew on macOS, the Windows Service Manager on Windows. The risk is the OS DNS setting getting quietly overwritten.

On Linux, NetworkManager resets /etc/resolv.conf when interfaces reconnect. The chattr +i approach blocks that, but the cleaner long-term solution is telling NetworkManager not to manage DNS at all:

# Create /etc/NetworkManager/conf.d/no-dns.conf [main] dns=none

On macOS, DHCP lease renewals push a new DNS server through System Settings. After any network change, verify with:

scutil --dns | grep nameserver

On Windows, domain Group Policy can silently override per-adapter DNS under HKLM\SOFTWARE\Policies\Microsoft\Windows NT\DNSClient. On managed corporate machines, check for conflicting GPO before assuming your config is active. Additionally, Chrome and Edge both ship with their own DNS-over-HTTPS enabled by default in 2026 — they bypass dnscrypt-proxy entirely. Disable Secure DNS in each browser: Chrome and Edge under Settings → Privacy and Security → Use secure DNS → off; Firefox via about:config → network.trr.mode = 5.

Deploying at the Router Level

Running dnscrypt-proxy on a router encrypts DNS for every device on the LAN without per-device configuration. This works on custom firmware and on a dedicated Linux host acting as a DNS server.

On OpenWrt (LuCI at 192.168.1.1 by default):

opkg update && opkg install dnscrypt-proxy2 # Edit /etc/dnscrypt-proxy/dnscrypt-proxy.toml # Set listen_addresses to ['127.0.0.1:5335'] to avoid conflict with dnsmasq uci set dhcp.@dnsmasq[0].server='127.0.0.1#5335' uci set dhcp.@dnsmasq[0].noresolv='1' uci commit dhcp service dnsmasq restart service dnscrypt-proxy enable && service dnscrypt-proxy start

On DD-WRT (192.168.1.1 or 192.168.0.1 depending on model): grab the static binary for your router's architecture (mipsel, aarch64, arm) and upload it to /jffs/. Add a startup script under Administration → Commands to launch it on boot. Under Services → Services → Additional DNSMasq Options, add server=127.0.0.1#5335 to point dnsmasq at the proxy.

Stock firmware routers — ASUS at asusrouter.com, TP-Link at tplinkwifi.net, Netgear at routerlogin.net, Orbi at orbilogin.com, Linksys at linksyssmartwifi.com — do not support dnscrypt-proxy natively. The practical workaround is running dnscrypt-proxy on a Raspberry Pi or small Linux box on the LAN, then pointing your router's DHCP server at that box's IP as the primary DNS server (usually under LAN → DHCP Server → DNS1 in stock firmware interfaces).

Troubleshooting

Port 53 Already in Use

On Linux, the culprit is almost always systemd-resolved on 127.0.0.53. Disable it as described in the install section. On macOS, mDNSResponder holds port 53. Bind dnscrypt-proxy to port 5300 and redirect traffic with pf:

# /etc/pf.anchors/dnscrypt rdr pass on lo0 proto udp from any to 127.0.0.1 port 53 -> 127.0.0.1 port 5300 rdr pass on lo0 proto tcp from any to 127.0.0.1 port 53 -> 127.0.0.1 port 5300 # In /etc/pf.conf, add: # anchor "dnscrypt" # load anchor "dnscrypt" from "/etc/pf.anchors/dnscrypt"

All Resolvers Failing or Unreachable

Check your system clock first. DNSCrypt uses time-bounded certificate exchange — if your clock is off by more than 10 minutes, authentication fails silently. On Linux: timedatectl status. On Windows: w32tm /query /status. Also check whether port 443 outbound is being intercepted by a corporate TLS inspection proxy — dnscrypt-proxy will correctly refuse those MITM connections, which looks like a complete resolution failure from the outside.

Slow Resolution After Install

Auto-selection may have picked a geographically distant resolver. Run dnscrypt-proxy -list to see live latency for each eligible server, then pin the fastest one explicitly in server_names. Verify caching is enabled — a cache_min_ttl of 0 disables the warm-cache performance benefit entirely.

DNSSEC Failures on Legitimate Domains

A small fraction of domains have broken DNSSEC chains — misconfigured DS records at the registrar or expired RRSIG signatures. If a domain resolves cleanly without DNSSEC enforcement but fails with require_dnssec = true, the problem is the domain's zone, not your proxy. Verify:

dig +dnssec +cd example.com @1.1.1.1

The +cd flag disables client-side validation so you see the raw response. Look for the ad (Authentic Data) flag in the answer header. Its absence on a supposedly signed domain points to a broken zone at the registrar or DNS provider.

Common Misdiagnoses

  • Service is running but DNS is still unencrypted. The proxy is up but the OS DNS is still pointing at the ISP's server. Confirm with resolvectl status (Linux), scutil --dns (macOS), or ipconfig /all (Windows) that 127.0.0.1 is the active resolver.
  • Websites noticeably slower after install. Cache is cold and auto-selection picked a distant resolver. Run -list, compare latencies, and switch to a regional server.
  • VPN breaks resolution entirely. VPN clients push their own DNS servers and override system DNS settings. Configure your VPN client to exclude DNS management, or ensure dnscrypt-proxy's chosen resolvers are reachable over the VPN tunnel.
  • require_nolog = true set but the router still shows DNS queries. This flag filters which upstream resolvers dnscrypt-proxy will connect to based on their stated policy — it does not prevent your local router or network equipment from logging DNS traffic at the packet level before it even reaches the proxy.
  • Android and iOS still leaking DNS. dnscrypt-proxy is a desktop tool. For mobile, Android 9+ supports Private DNS (Settings → Network → Advanced → Private DNS) pointing to a DoT hostname. iOS 14+ accepts DNS configuration profiles. There is no dnscrypt-proxy mobile client.

2026 Considerations

Browser-native DoH is universal by 2026 — Chrome, Firefox, Edge, and Safari all enable it by default. This means browser DNS queries bypass dnscrypt-proxy entirely and go directly to the browser's configured DoH resolver, even when the proxy is running correctly at the system level. Disable browser-level Secure DNS in each browser to route all DNS through dnscrypt-proxy: Firefox requires network.trr.mode = 5 in about:config; Chrome and Edge have a toggle under Settings → Privacy and Security → Use secure DNS → off.

DNSSEC-signed zones now cover the overwhelming majority of commercial TLDs. Setting require_dnssec = true is safe for general use in 2026 and provides protection against Kaminsky-style cache poisoning that DoH alone does not prevent — encryption and authentication are separate properties.

ODoH (Oblivious DoH) moved from IETF draft to deployed infrastructure in 2025. If your threat model includes the resolver operator correlating your IP with your query history, ODoH stamps in the public resolvers list route through a separate relay that strips your IP before the query reaches the resolver. Neither party alone can correlate your identity with your lookups.

IPv6 AAAA records are now the primary record type for most CDN-backed services. A dnscrypt-proxy config that only listens on 127.0.0.1 leaves IPv6 DNS queries unprotected on dual-stack systems. Always include [::1]:53 in your listen_addresses — this is one of the most commonly skipped steps in older setup guides that are still circulating in 2026.