On macOS, fix a DNS leak by setting resolvers per network service in System Settings, Network, Wi-Fi, Details, DNS, then flushing with dscacheutil -flushcache and killall -HUP mDNSResponder, and verifying the real resolution order with scutil --dns.
How macOS actually chooses a resolver
macOS resolution is handled by mDNSResponder, which reads a set of resolver configurations rather than a single list. Each network service (Wi-Fi, Ethernet, a VPN interface, Thunderbolt Bridge) contributes its own resolver entry, and additional scoped entries are added for specific domains, for multicast DNS on .local, and for anything a VPN client installs.
The consequence is that the resolver shown in System Settings is not necessarily the resolver used for a given lookup. A VPN can install a scoped resolver that only handles the corporate domain, leaving everything else on your ISP's server. That is a leak by any reasonable definition, and it will not be visible in the graphical interface.
The service order also matters. System Settings, Network, then the three-dot menu, then Set Service Order determines which interface is preferred when several are active. A VPN that appears below Wi-Fi in that list can be overridden for general traffic.
Diagnose the leak with scutil and dig
scutil --dns is the authoritative view. It prints the full resolver stack in priority order, showing each resolver's servers, the domains it is scoped to, and its search order value. Lower order numbers win.
Read the output from the top. The first unscoped resolver, meaning one without a domain : line restricting it, handles ordinary lookups. If that resolver's nameserver[0] is your router's address or an ISP resolver while your VPN is connected, you have found the leak.
resolver #1withflags: Request A records, Request AAAA recordsand no domain scope is the default path.nameserver[0] : 192.168.1.1means queries go to your router, which forwards to your ISP.- The
dig +short whoami.akamai.netanswer is the egress address of the resolver, which you can then check for ASN ownership. - The CHAOS
id.serverquery against 1.1.1.1 returns the Cloudflare edge node identifier, confirming the query genuinely reached Cloudflare rather than being intercepted.
scutil --dns | head -n 40
networksetup -listallnetworkservices
networksetup -getdnsservers Wi-Fi
dig +short whoami.akamai.net
dig +short TXT o-o.myaddr.l.google.com @ns1.google.com
dig +short chaos TXT id.server @1.1.1.1macOS ignores /etc/resolv.conf for its own resolution. That file is maintained for compatibility with command-line tools linked against the BSD resolver. Editing it does not change what Safari or Chrome do, and changes there are overwritten.
Pin DNS per network service
In the graphical interface: System Settings, Network, select Wi-Fi, click Details next to the connected network, choose DNS in the sidebar, then use the plus button under DNS Servers to add addresses. Remove greyed-out DHCP-supplied entries by adding your own above them; the greyed entries stop being used once explicit servers exist. Click OK, then Apply if prompted. Repeat for every service in the list, including Ethernet adapters you are not currently using.
The command line applies the same change without clicking through each service, and is scriptable for machines that move between networks.
Order matters as much as the values. macOS consults services in the order shown under System Settings, Network, then the three-dot menu, then Set Service Order. If a Thunderbolt Bridge or an unused USB Ethernet adapter sits above Wi-Fi, its resolver configuration can take precedence for lookups even though no cable is attached. Drag the interfaces you actually use to the top, and set explicit resolvers on the rest rather than leaving them on DHCP.
Be aware that a configuration profile installed by an employer, a mobile device management enrolment, or certain security agents can pin resolvers at a level you cannot override from System Settings. If your changes appear to save but scutil --dns still shows something else, check System Settings, General, Device Management for an installed profile containing a DNS payload.
sudo networksetup -setdnsservers Wi-Fi 1.1.1.1 1.0.0.1
sudo networksetup -setdnsservers "USB 10/100/1000 LAN" 1.1.1.1 1.0.0.1
networksetup -getdnsservers Wi-Fi
# revert to DHCP-supplied resolvers
sudo networksetup -setdnsservers Wi-Fi emptyFor encrypted DNS system-wide, install a DNS settings configuration profile (a .mobileconfig containing a DNSSettings payload with DNSProtocol set to HTTPS or TLS). This is the supported mechanism on modern macOS and it applies to all applications, unlike a browser-level setting. Manage it under System Settings, General, Device Management.
Flush the cache correctly
macOS needs two commands, not one. dscacheutil -flushcache clears the Directory Service cache, and killall -HUP mDNSResponder signals the resolver daemon to reload its configuration and drop its own cache. Running only the first is the most common reason a change appears not to take effect.
You can also watch resolution live, which is the fastest way to prove where queries are going without a third-party test page. The unified log shows each query and the interface it left on.
sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder
sudo log stream --predicate 'process == "mDNSResponder"' --info | grep -i "Question\|to 1.1.1.1\|to 192.168"
sudo tcpdump -n -i any port 53 or port 853IPv6, split tunnels and per-app leaks
If your ISP provides native IPv6 and your VPN carries only IPv4, AAAA lookups and the connections that follow them bypass the tunnel. macOS follows Happy Eyeballs (RFC 8305) and will prefer IPv6 when it is available and fast, so this is not an edge case.
Disable IPv6 per service if your VPN does not tunnel it, and re-enable it when you change providers. Configure IPv6 is also available in the graphical path at System Settings, Network, Wi-Fi, Details, TCP/IP, where the dropdown offers Automatic, Manually, Link-local only, and Off.
Separately, check the browsers. Firefox and Chrome each have their own DNS over HTTPS setting that overrides the system resolver for that application only. A perfectly configured Mac can still send every browsed domain to a third party because a browser toggle is on. Safari uses the system resolver and does not have an independent setting, which makes it useful as a control when testing.
networksetup -getinfo Wi-Fi
sudo networksetup -setv6off Wi-Fi
sudo networksetup -setv6automatic Wi-FiTurning IPv6 off is a workaround, not a fix. A growing share of mobile carriers run IPv6-only access networks with NAT64, and some services are reachable only over IPv6. Prefer a VPN configuration that tunnels both address families.
Confirm the fix survives a reconnect
Re-run scutil --dns with the VPN connected and confirm that the first unscoped resolver is the one you intend. Then disconnect the VPN, reconnect, and run it again. VPN clients rewrite the resolver stack on every connect, and a fix that only holds until the next reconnect is not a fix.
Test the interruption case too. Put the Mac to sleep briefly and wake it, or toggle Wi-Fi off and on while the VPN client is running. Watch the log stream during the first ten seconds after the link returns. If queries go to the router in that window, the client has no functioning leak protection during reconnection and you should enable whatever it calls a kill switch or firewall lockdown.
Keep a one-line check you can run any time rather than relying on memory. Comparing the resolver egress address against your public address takes a few seconds and catches configuration drift after macOS updates, which do occasionally reset network service settings.
echo "public: $(dig +short myip.opendns.com @resolver1.opendns.com)"; echo "resolver: $(dig +short whoami.akamai.net)"
Frequently asked
Why does scutil --dns show my ISP resolver even after I changed DNS in System Settings?
Either you changed it on one network service while another active service still has DHCP-supplied resolvers, or a VPN client installed a higher-priority resolver configuration after you made the change. Check every service with networksetup -listallnetworkservices, set each one explicitly, and re-run scutil --dns after a VPN reconnect.
Does editing /etc/resolv.conf change DNS on macOS?
No. macOS resolution goes through mDNSResponder, which reads System Configuration data, not that file. The file exists so that BSD-linked command-line tools have something to read, and macOS regenerates it from the active configuration. Any manual edit is discarded on the next network change.
What is the difference between dscacheutil -flushcache and killall -HUP mDNSResponder?
The first clears the Directory Service cache used for user and group lookups as well as some host records. The second sends a hangup signal to the resolver daemon, causing it to reload configuration and discard cached DNS answers. Run both, in that order, for a complete flush.
Can I force encrypted DNS for every app on macOS?
Yes, using a DNS settings configuration profile containing a DNSSettings payload with DNSProtocol set to HTTPS or TLS. Once installed, it applies system-wide and is visible under System Settings, General, Device Management. This is more reliable than per-browser settings, which only cover that browser's own traffic.
Does macOS leak DNS on a captive portal network?
Briefly, yes, and by design. Before authentication, macOS must resolve the portal host using the network's own resolver, and it probes captive.apple.com to detect the portal. This traffic is unavoidable. Connect the VPN only after the portal is cleared, and assume the portal operator saw those first lookups.