You connect the tunnel, open a private dashboard, and the name resolves to an address you have never seen before. Or the opposite happens: the browser keeps working while a command line tool suddenly resolves nothing. Both outcomes come from the same change, and people usually explain it with one sentence about the VPN "using its own DNS", which is not precise enough to debug or to verify. Two questions are hidden in that sentence: which lookups is this profile supposed to take over, and did your machine actually send them to the resolver it was handed? Keeping them apart is the whole method, because a test that mixes them produces a confident wrong answer in both directions.

Why a profile supplies a resolver at all

Inside a tunnel, names behind the far end usually resolve to addresses that exist only in that network. A private zone such as corp.internal has no public delegation, so the resolver that answers it must be reachable from inside the tunnel. The same mechanism serves a second purpose: when lookups would otherwise leave through your ISP, a resolver on the far side keeps the query inside the encrypted path. That is why the DNS server address arrives as part of the connection's policy rather than as a global setting you chose once for the machine.

The address on its own settles nothing. A resolver can answer only if there is a route to it, and the most common failure is a resolver reachable only while the tunnel is up. When the tunnel drops and the policy remains, lookups go to an address with no path, and the symptom is a timeout rather than a clean error. That difference matters when you read the next test result, because a timeout sends people into transport settings that were never involved.

Compare the two designs before comparing answers

Full tunnel and split DNS produce opposite expectations, and neither one is a malfunction. Under a full tunnel every lookup may move to the provider or to the corporate resolver, so public names answering differently after connect is expected. Under split DNS only listed domains go to the private resolver while everything else stays local; on Windows the domain list lives in the Name Resolution Policy Table, on macOS in the DNS settings' match domains, and on a systemd-resolved host in per-link routing domains.

That is the comparison to make first, because it decides what counts as a bug. With split DNS, a public name answering from a new resolver is the thing worth investigating, while an internal name answering correctly is the system doing its job. With a full tunnel, a public name that still answers from your ISP resolver is the anomaly. Microsoft's documentation on VPN name resolution describes the Windows ordering of these rules, including how a matching policy entry is consulted before the adapter's own servers.

There is a third resolver in the room that is easy to forget. Browser secure DNS resolves names on its own and can keep answering after the tunnel changed the operating-system policy, so a page that loads proves the browser's resolver worked, not that the profile was applied. If you compare only what the browser shows, you are testing the wrong layer half the time.

Record a baseline with only one variable

Change the tunnel. Do not also change the name, the tool, or the search suffix in the same step. Pick a name you are allowed to query, note which application you used, and write down either the address that came back or the exact error. On Windows, Get-DnsClientServerAddress -AddressFamily IPv4 prints the per-adapter servers and Get-DnsClientNrptPolicy prints the policy entries; on Linux with systemd-resolved, resolvectl status shows per-link DNS servers and routing domains; on macOS, scutil --dns prints the resolver order the system will actually follow.

Then connect through the client's normal controls and repeat exactly the same commands. Keep the first output rather than a memory of it, because the comparison is the entire point and a second reading taken after a cache flush is already a different measurement. If the baseline was taken with the browser in front and the after-connect run happens in a terminal, you are comparing two resolvers, not two states of one resolver.

An explicit query and an application lookup are different tests

Asking one specific server, with nslookup name 10.20.0.1 or dig @10.20.0.1 name, proves a single thing: that the server you named answers when you can reach it. It bypasses the selection logic completely, so it cannot tell you whether the system would have chosen that server for this name. Use it to test whether the assigned resolver is reachable, and do not use it to test policy.

A lookup through the local resolver does exercise selection. resolvectl query name walks the same routing-domain rules and reports the protocol that answered, Resolve-DnsName on Windows goes through the policy table and the client cache, and scutil --dns shows the ordering a lookup would follow. Run both kinds, and keep them in separate columns of your notes. A green explicit query next to a failing application lookup is a routing or selection problem, not a resolver that refuses to work.

Caching sits between you and the answer. The operating system, the local stub resolver, the browser, and negative caching all hold results, so a lookup can succeed without a single packet leaving the machine. Record the result before flushing anything: ipconfig /flushdns, resolvectl flush-caches, or sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder on macOS. Skip the broad network reset at this stage, since it destroys the evidence you are still collecting.

What the difference means

Compare the two rows you recorded and classify the change before touching a single setting.

Observation after connectMost likely causeNext step
Answer moves to a private address rangeExpected under split DNS or a private zoneConfirm the suffix is in the effective policy, then test the service
Lookup times out while connected, works after disconnectThe assigned resolver has no route from this machineProbe it with an explicit query, then re-read the policy
Internal name fails, public names workWrong namespace or a suffix missing from the policyQuery the fully qualified name and check the domain list per platform
Internal name returns a public addressSplit DNS not applied; a public resolver answeredCheck browser secure DNS first, then the per-link domain list

One row deserves emphasis. A name that resolves is not a service that works, and DNS success says only that an address was found. It says nothing about forwarding, firewall rules, or whether your account may use the service at all, so finish by testing the actual application rather than assuming the failure moved away on its own.

Restore the baseline and verify it

Disconnect through the client's own controls instead of killing the process, then re-read the same three commands. The per-link servers, the policy entries, and the answer for your test name should return to the baseline you wrote down earlier. A leftover policy entry after a clean disconnect is a real defect worth recording, and it is also why hand edits to a generated resolver file are a bad habit: the file gets rewritten and the edit disappears, or the edit survives and pins a resolver that no longer has a route.

Work through this list once, in order. Decide which design the profile implements before judging any result. Keep name, tool, and suffix fixed across the two runs. Separate the explicit query from the application lookup. Read the effective policy, not only the answer. Flush caches last, after the comparison is written down. Verify with the real application, then reboot once and confirm the disconnect state is still clean.

Barruulee wal qabatan