AD integrated DNS is a great way to get name resolution going in a Windows-heavy network. Changes to DNS zone data are replicated across the environment using robust AD replication mechanisms, providing a globaly consistent data pool with optimal bandwidth utilization. Sometimes, however, we have to find other means to achieve configurations best suited for our organizations‘ network topologies. In this post, I would like to talk about DNS forwarders.
This post describes delivering configurations to DNS servers running on Domain Controllers. Many of the same techniques can be applied to any Windows DNS servers as long as they are domain-joined.
If you operate geographically distributed Active Directory forests, you might end up in a situation where certain sites have their own Internet uplinks that may be way faster than the WAN link connecting those sites with the central hub. And if you need name resolution to a partner organization or to a different AD forest within your own infrastructure, it may also be beneficial to target particular name servers for resolving names in that other forest, depending on where you are within your own network. In short, managing external (to your forest) DNS resolution on a per-site basis may help speed up name resolution quite significantly if you have a complex network topology to contend with.
This may not be a mainstream challenge, but I have been asked about it for the third time in the course of two years, so the critical mass for a blog post is reached 🙂
What are DNS forwarders?
If a DNS Server does not host the zone referenced in a query, and doesn’t have the reply cached from responding to a previous query, it can do two things:
- walk the DNS zone tree from the root zone (.) to TLD (.com) to the first-level domain (ad2049.com) to the host (www.ad2049.com), each time querying for an authoritative server of the respective zone…
- …or pass the query to another server which then will do the tree walk or maybe ends up caching the correct entry itself.
The first scenario has nothing to do with forwarders. If that’s how you want to do public name resolution, you either configure your server as a „slave“ and have it host a copy of the root zone locally (which you can get from INTERNIC using HTTP or FTP and edit if you do not wish for certain TLDs to be resolved in your organization in the first place), or you can make it a „master“ (of everything it is hosting itself) and have it reach out to root DNS servers to obtain the SOA and NS for the TLD your tree walk starts at. The list of root servers (the „root hints“) can also be obtained from INTERNIC (HTTP / FTP); Microsoft ships it with Windows DNS by default. We will look at root hints at the end of this post.
In the second scenario, the DNS server being queried makes use of a „forwarder“ – another DNS server that is expected to be able to provide, albeit indirectly, the required information. Windows DNS maintains two types of forwarders:
- Conditional Forwarders specify DNS servers that should be able to resolve names in a particular zone.
- Global Forwarders specify DNS servers a query is forwarded to if the local DNS server does not host the zone and there is no conditional forwarder configured for that zone.
You will usually use Conditional Forwarders for the „partner organization“ or „trusted forest“ scenario and Global Forwarders for external name resolution. I have seen them used differently in the field, too, but those cases are well outside of the scope of this post.
Distributing DNS Forwarders
Let’s look at how they are configured in Windows and how to make them different from one site to another.
Conditional Forwarders
Whenever you add or edit a conditional forwarder, you have the option (unchecked by default) to store it in Active Directory:

The replication scope selector will determine the partition the forwarder will be stored in: DomainDNSZones, ForestDNSZones or domain partition for Windows 2000 compatibility. If you leave the box unchecked, however, the forwarder will be stored locally on the DNS server in question. DNS will not create a zonefile for it, but it will add the zone registration in the registry:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\DNS Server\Zones\partner-forest.com] "Type"=dword:00000004 "MasterServers"=hex(7):31,00,30,00,2e,00,30,00,2e,00,31,00,30,00,33,00,2e,00,\ 31,00,32,00,00,00,31,00,30,00,2e,00,30,00,2e,00,31,00,30,00,33,00,2e,00,31,\ 00,31,00,00,00,00,00 "SecureSecondaries"=dword:00000003 "NotifyLevel"=dword:00000002 "ForwarderTimeout"=dword:00000005 "ForwarderSlave"=dword:00000000 "EnablePolicies"=dword:00000000 |
The important values, beside the zone name, are Type (4 = conditional forwarder), ForwarderSlave (0 = not a slave) and, of course, MasterServers (a MULTI_SZ holding the IP addresses of the forwarding targets).
To distribute Conditional Forwarders on a per-site or per-DC basis, create them as required on one Domain Controller and make sure not to check the box for storing them in AD. Then create a GPO and in the Preferences section under Windows Settings\Registry, add a Registry Wizard. Target the DC where you set the forwarders and import the registry keys specified above, including all the values:

(I renamed the default „Registry Wizard Values“ to „Conditional Forwarders“ here.)
Since the only value that will vary between the sites is „MasterServers“, select it, got to Properties –> Common –> Item-Level targeting and add a Site = HQ condition:

Copy this item and paste it back as often as you have different configuration and edit the ILT condition for each site:

Repeat for every external zone you created a Conditional Forwarder for. Now all that is left to do is link this GPO appropriately (Domain Controllers OU in every domain of the forest would probably be a good choice). In case the forwarder timeout needs to vary between sites, you can apply the same logic to the ForwarderTimeout value!
Global Forwarders
Global forwarders cannot be stored in AD, they are local by nature; they are stored in the DNS Server service configuration, i.e. again, in the Registry:
|
1 2 3 4 5 6 7 |
Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DNS\Parameters] "Forwarders"=hex(7):38,00,2e,00,38,00,2e,00,38,00,2e,00,38,00,00,00,39,00,2e,\ 00,39,00,2e,00,39,00,2e,00,39,00,00,00,00,00 "ForwardingTimeout"=dword:00000003 "IsSlave"=dword:00000001 |
You SHOULD NOT be distributing the entire key in this case, just the values listed above. I will cover IsSlave in more detail a minute.
The distribution and ILT logic is the same as for Conditional Forwarders:

What about Root Hints?
Yeah, about that… You could say, with good confidence, that Root Hints in Windows DNS are a mess. I will share what I know about their storage and distribution below, but before you dive in and find yourself in the rabbit hole of uncertainty and undocumentedness, ask yourself this:
- Do my AD integrated DNS servers really need to resolve public DNS records in the first place? Chances are a proxy is implemented for external access, and then that proxy is the one in need of external name resolution.
- If indeed they do, and none of the global forwarders is answering, do I really want to fall back to the root hints?
Because if the answer to either question is „no“, just disable the fallback to root hints. This, too, is done by modifying the registry and restarting the service. To disable root hint fallback, set
|
1 2 3 4 |
Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DNS\Parameters] "IsSlave"=dword:00000001 |
and to enable root hint fallback, set
|
1 2 3 4 |
Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DNS\Parameters] "IsSlave"=dword:00000000 |
It is important to remember, that, while a DNS server can be confiugured to be „not a slave“ and yet not to have any root hints, Microsoft will not support that, unless the server hosts a copy of the root zone – in this case, it does indeed not need root hints! In fact, especially in AD integrated DNS, Microsoft went to great lengths to not allow this configuration to happen. This is achieved by storing the root hint list in multiple locations and falling back to them if the primary location has been stripped of all root hints.
So where *are* the root hints stored then?
The short answer is:
- for AD integrated DNS: in the DomainDNSZones partition of the domain the Domain Controller running DNS belongs to (you will also find root hints stored in each domain partition, but that’s a relic from Windows 2000 days – to the best of my knowledge, these entries are never used in later versions)
- for standalone DNS, even if it is domain-joined: in the flat text file „C:\Windows\System32\dns\cache.dns“
- in any case: in the service’s memory as long as the service is running.
The cache.dns file is also present on AD integrated DNS servers and serves as fallback data source in case the records stored in AD has been removed.
There is, however, a subtle difference in how edits to root server entries are handled, at least on modern Windows versions.
To IPv6 or not to IPv6 – that is the question
If you edit root hints on a standalone DNS server, the edit goes to the cache.dns file. By default, that file lists one IPv4 and one IPv6 entry for every root server:

Edits you make in DNS Manager are written directly to that file (and are committed to the service memory so that the changes become active without restarting the service).
If you look at the root hints of an AD integrated DNS server, you will notice that both the DNS Manager and the records in AD (once you have decoded the dnsRecord binary data) only show IPv6 addresses! And here’s the scary part: at least in Server 2022 and 2025 you can only create root hint entries with addresses from the same family using the inbox tooling:
- you can edit or add IPv6 addresses to root hint entries that already contain IPv6 addresses
- you can also replace IPv6 addresses with IPv4 addresses
- if you add an IPv4 address to a root hint entry that previously contained an IPv6 address, the IPv4 address will not be written to AD; it is lost after service restart
- if you add an IPv6 address to a root hint entry that previously conrained an IPv4 address, the IPv6 address is added visually, but in AD it replaces any IPv4 addresses so that upon service restart, the IPv4 information is lost.
This behaviour is consistent between DNS Manager MMC snap-in and Add-DNSServerRootHint in PowerShell. If you inject a mixed-family entry directly into AD, AD integrated DNS will load and use the IP addresses of both families. If you do that, you cannot edit *any* root hint using DNS Manager anymore since *all of them* will be „v6-ified“ on save! Editing a single root hint via PowerShell does not influence other root hints.
I still want to use Root Hints, how can I distribute them?
Firstly, since the cache.dns file is (at least theoretically) used as fallback in case the root hint data cannot be retrieved from AD, it is a good idea to distribute that file even if AD integrated DNS is being used. For that, you can put template files in a central shared location and use the Files GPP to update them. For standalone domain-joined DNS servers, that’s all you have to do.
Secondly, since AD integrated DNS pulls data from a domain-wide replicated partition, this represents your level of granularity. You cannot create different root hints in different sites of the same domain. You should, however, maintain consistency between the cache file you distribute and the root hints stored in AD.
Restarting the DNS Server service automatically on configuration changes
You can push the forwarders into your Domain Controllers‘ registry all day, but they will not take effect until the DNS Server service is restarted. You can get creative here, restart DNS every night using a Scheduled Task, or implement something that tracks changes in the registry but this is what I found.
In the same policy you created the registry settings for your Forwarders, under Preferences\Control Panel Settings\Services, add a service entry for DNS. Make sure to leave all parameters at „No change“ but set the service action to „Restart“:

To have the DNS service automagically restart every time you edit the GPP, add the following compound ILT condition:

The resulting behaviour is not 100% what you might infer from the documentation, but here is what I am consistently seeing:
- After initial application, gpupdate without changes to the policy DOES NOT cause a service restart
- In any case, gpupdate /force DOES cause a service restart
- Changes to other GPOs DO NOT cause a service restart
- Changes to the Policies part of the same GPO DO NOT cause a service restart
- Any changes to the Preferences part of the same GPO DO cause a service restart
Magic! There is one caveat here though: Windows seems to process the Services GPP extension before the Registry one. If you look at the DNS server after making a change to the policy, you will see the correct registry values in place and the service restarted, but if you query the running DNS server configuration, it’s still the previous one. To work around this, you can create an „immediate scheduled task“ instead, with the same ILT conditions and the actions of
|
1 2 3 |
timeout /t 30 /nobreak >nul net stop dns net start dns |
This could look like this:



Happy Forwardering!
Title image by Franck Barske from Pixabay