Type something to search...
What Is Split-Horizon DNS?

What Is Split-Horizon DNS?

Imagine your staff type app.example.com into a browser at the office and reach the application server directly on the local network, while customers typing the exact same name from home land on a public load balancer. Nobody has to remember two hostnames, certificates match in both places, and internal IP addresses never leak onto the internet. That arrangement is called split-horizon DNS, and it is one of the most common patterns in corporate and cloud networks. If you have already read about the difference between public and private DNS, split-horizon is what happens when you deliberately run both for the same domain.

This article explains what split-horizon DNS is, how servers decide which answer to give, how to configure it in BIND, Unbound, Windows Server, and AWS Route 53, and the mistakes that cause the most trouble once it is in production.

What Is Split-Horizon DNS?

Split-horizon DNS (also called split-brain DNS or split-view DNS) is a configuration where a DNS server, or a set of servers, returns different answers for the same query depending on where the query comes from. The "horizon" is the boundary between two audiences — usually your internal network and the public internet.

A typical setup looks like this:

Query sourceQuestionAnswer
Laptop on the office LANapp.example.com A10.0.1.20 (private server)
Phone on mobile dataapp.example.com A203.0.113.20 (public load balancer)
Laptop on the office LANintranet.example.com A10.0.1.30
Phone on mobile dataintranet.example.com ANXDOMAIN (does not exist publicly)

Both audiences use the same domain name, but each sees a different version of the zone. The internal version can contain extra records that the public never sees, and it can point shared names at private addresses.

Why Organizations Use It

  1. One name everywhere. Users, bookmarks, mobile apps, and TLS certificates all reference app.example.com. You do not need a separate app-internal.example.com that people forget to use.
  2. Shorter, cheaper network paths. Internal clients reach the server directly instead of hairpinning out through the firewall and back in through a public IP. Many firewalls handle hairpin NAT poorly or not at all.
  3. Hiding internal topology. Hostnames like db-primary.example.com or jenkins.example.com reveal what you run. Keeping them in the internal view only means they cannot be enumerated from outside.
  4. Hybrid cloud and VPNs. Workloads in a VPC can resolve private endpoints while the same names resolve to public endpoints for everyone else.
  5. Staged migrations. You can point internal users at a new backend first, test it under real traffic, and only then change the public record.

How It Works Under the Hood

DNS itself has no concept of "internal" or "external." Split-horizon is entirely a server-side decision based on properties of the incoming query. The most common signals are:

  • Source IP address. The server checks whether the client (or the recursive resolver forwarding on its behalf) falls inside a list of internal subnets such as 10.0.0.0/8.
  • The interface or address the query arrived on. A server might listen on a LAN-facing IP and a public IP and serve different data on each.
  • TSIG keys. BIND can match a view by the key used to sign the request, which is useful for giving secondaries a specific view during zone transfers.
  • Network membership in the cloud. Route 53 private hosted zones and Azure Private DNS answer based on which VPC or virtual network the query originates from.

There are two architectural styles:

  • Single server with views. One authoritative server holds two copies of the zone and picks one per query. BIND's view statement is the classic example.
  • Separate servers. Internal clients use an internal resolver that holds (or forwards to) the private zone, while the public uses your normal authoritative provider. Most companies with Active Directory end up here, because domain controllers serve the internal zone and a managed DNS host serves the public one.

Either way, the result is the same: recursive and authoritative servers in different locations hold different truths about the same name.

Configuring Split-Horizon DNS in BIND

BIND implements split-horizon with view blocks. Each view has a match-clients list, and the first view that matches a query wins. Once you use views, every zone in named.conf must live inside a view.

// /etc/bind/named.conf.local
acl "internal-nets" {
    10.0.0.0/8;
    192.168.0.0/16;
    localhost;
};

view "internal" {
    match-clients { internal-nets; };
    recursion yes;

    zone "example.com" {
        type primary;
        file "/etc/bind/zones/db.example.com.internal";
    };
};

view "external" {
    match-clients { any; };
    recursion no;

    zone "example.com" {
        type primary;
        file "/etc/bind/zones/db.example.com.external";
    };
};

The internal view matches LAN clients, allows recursion, and loads the private zone file. Everyone else falls through to the external view, which is authoritative-only and serves the public zone. Order matters: if external came first, its any would match every client and the internal view would never be used. On Debian and Ubuntu, the default named.conf.default-zones include also needs to be moved inside a view (usually the internal one), or named-checkconf will complain that zones exist outside views.

The two zone files then differ only where they need to:

; /etc/bind/zones/db.example.com.internal
$TTL 300
@          IN  SOA   ns1.example.com. hostmaster.example.com. (
                     2026100101 3600 900 604800 300 )
           IN  NS    ns1.example.com.
ns1        IN  A     10.0.0.53
www        IN  A     203.0.113.10
app        IN  A     10.0.1.20
intranet   IN  A     10.0.1.30
; /etc/bind/zones/db.example.com.external
$TTL 300
@          IN  SOA   ns1.example.com. hostmaster.example.com. (
                     2026100101 3600 900 604800 300 )
           IN  NS    ns1.example.com.
ns1        IN  A     203.0.113.53
www        IN  A     203.0.113.10
app        IN  A     203.0.113.20

Notice that www appears in both files with the same value. This is the part people forget: the internal view is a complete, separate zone, not an overlay. Any public record you do not copy into it simply does not exist for internal users.

Validate and reload with the standard tools:

sudo named-checkconf
sudo named-checkzone example.com /etc/bind/zones/db.example.com.internal
sudo named-checkzone example.com /etc/bind/zones/db.example.com.external
sudo rndc reload

named-checkconf catches syntax and view placement errors, named-checkzone validates each zone file independently, and rndc reload applies the change without restarting the daemon. For a full walkthrough of installing and hardening BIND, see how to run your own DNS server with BIND.

Overriding a Few Names with Unbound

If you already run a recursive resolver for your LAN and only need to override a handful of names, you do not need a full second copy of the zone. Unbound's local-zone and local-data directives let you answer specific names locally and resolve everything else normally:

# /etc/unbound/unbound.conf.d/split-horizon.conf
server:
    local-zone: "example.com." transparent
    local-data: "app.example.com. 300 IN A 10.0.1.20"
    local-data: "intranet.example.com. 300 IN A 10.0.1.30"
    private-domain: "example.com."

The transparent zone type means that names with local data are answered from the config, while any other name under example.com (like www) is resolved from the public authoritative servers as usual. private-domain tells Unbound's rebinding protection to allow private addresses in answers for this domain. Pi-hole and dnsmasq offer the same kind of per-name override, which is often all a small office needs.

Configuring It on Windows Server DNS

Windows Server 2016 and later support split-brain DNS natively through DNS policies and zone scopes. You create a client subnet, add a scope to the zone, put the internal record in that scope, and add a policy that sends matching clients to it:

Add-DnsServerClientSubnet -Name "InternalSubnet" -IPv4Subnet "10.0.0.0/8"

Add-DnsServerZoneScope -ZoneName "example.com" -Name "InternalScope"

Add-DnsServerResourceRecord -ZoneName "example.com" -A -Name "app" `
    -IPv4Address "10.0.1.20" -ZoneScope "InternalScope"

Add-DnsServerQueryResolutionPolicy -Name "SplitBrainPolicy" -Action ALLOW `
    -ClientSubnet "eq,InternalSubnet" -ZoneScope "InternalScope,1" `
    -ZoneName "example.com"

Queries from 10.0.0.0/8 get answers from InternalScope, and everyone else gets the default scope of the zone. Unlike BIND views, records that only exist in the default scope are not automatically visible to the internal scope, so test carefully.

Split-Horizon in AWS Route 53

In AWS, split-horizon is a pairing of a public hosted zone and a private hosted zone with the same name, associated with one or more VPCs. Resources inside those VPCs that use the Amazon-provided resolver see the private zone; everyone else sees the public one.

aws route53 create-hosted-zone \
    --name example.com \
    --caller-reference internal-example-2026-10-01 \
    --vpc VPCRegion=us-east-1,VPCId=vpc-0abc1234def567890 \
    --hosted-zone-config Comment="Internal view",PrivateZone=true

This creates a private zone attached to the given VPC. As with BIND, the private zone does not fall back to the public one — if a name is missing in the private zone, VPC clients get NXDOMAIN even when the public zone has it. The Route 53 guide covers adding records to either zone.

Testing Both Sides of the Horizon

The easiest way to verify split-horizon is to ask both servers the same question explicitly with dig:

# Ask the internal resolver
dig @10.0.0.53 app.example.com A +short
# 10.0.1.20

# Ask a public resolver
dig @1.1.1.1 app.example.com A +short
# 203.0.113.20

# Make sure shared names still resolve internally
dig @10.0.0.53 www.example.com A +short
# 203.0.113.10

If the third query returns nothing, your internal zone is missing a public record. It is worth scripting a check that loops over every public name and confirms it also resolves through the internal server, then running it after every DNS change.

Common Problems and How to Avoid Them

Records Missing from the Internal View

This is the number one split-horizon failure. Someone adds a TXT record for a new SaaS tool, an _acme-challenge record, or a new subdomain to the public zone, and internal users suddenly cannot reach it. Mitigations:

  • Keep both views in version control and generate them from a single source, with internal overrides applied on top.
  • Use resolver-level overrides (Unbound transparent, Windows zone scopes for single names) instead of a full duplicate zone when you only need a few changes.
  • Delegate a dedicated internal subdomain such as corp.example.com for purely internal names, so the main zone does not need to be split at all.

Caching Across the Boundary

Laptops move. A machine that resolved app.example.com to 10.0.1.20 in the office and then joins a hotel network may keep using the cached private address until the TTL expires. Keep TTLs on split records short (300 seconds or less), and remember that clients may need to flush the DNS cache after changing networks.

VPN Clients Using the Wrong Resolver

Split-tunnel VPNs often send only corporate traffic through the tunnel while leaving DNS on the local network. If queries go to the hotel's resolver, internal names fail. Configure the VPN to push the internal DNS server, ideally scoped to your internal domains.

Encrypted DNS in Browsers

Browsers with DNS over HTTPS enabled may send queries straight to a public resolver, bypassing your internal server and breaking internal names. Managed devices can disable this through enterprise policy, and Firefox checks the canary domain use-application-dns.net — if your internal resolver returns NXDOMAIN for it, Firefox will not enable DoH automatically. See what DNS over HTTPS is for more background.

DNSSEC Complications

If the public zone is signed with DNSSEC, validating resolvers expect every answer for that zone to carry valid signatures. Unsigned internal answers for a signed public zone will fail validation on any client or resolver that validates. Options include signing the internal view with the same keys, configuring internal resolvers to treat the zone as an insecure local domain, or moving internal names to a separate delegated subdomain.

Avoid .local and Made-Up TLDs

Using .local conflicts with multicast DNS on macOS and Linux, and invented TLDs can collide with future real ones. Use a subdomain of a domain you own (for example corp.example.com) or the reserved .internal TLD that ICANN set aside for private use in 2024.


Split-Horizon DNS FAQ

Yes. Split-horizon, split-brain, and split-view DNS all describe the same idea: one domain name, different answers depending on who is asking. Split-brain is the term Microsoft uses most often in its documentation.

It helps by keeping internal hostnames and private IP addresses out of public DNS, which makes reconnaissance harder. It is not access control, though. Anyone who reaches your internal resolver can query the internal view, so firewalls and authentication still need to protect the services themselves.

No. BIND views and Windows DNS policies let a single server serve both versions of a zone. Many organizations still use separate internal and external servers because it keeps the public infrastructure simpler and fully managed.

Usually because the internal view is a full copy of the zone and the public record was never added to it. Internal clients get NXDOMAIN for anything missing. Add the record to the internal view or switch to a per-name override approach.

Yes. The certificate only cares about the hostname, which is identical in both views. If you use the DNS-01 challenge, make sure the _acme-challenge TXT record is published in the public zone, because Let's Encrypt validates from the internet.

Cloud providers implement it with private zones attached to virtual networks. Route 53 private hosted zones, Google Cloud private zones, and Azure Private DNS zones answer queries from associated networks, while the public zone of the same name answers everyone else.

Keep split records short, around 60 to 300 seconds, so devices that move between networks do not hold the wrong answer for long. Records that are identical in both views can use your normal TTLs.

It can. If the public zone is signed, validating resolvers will reject unsigned internal answers for the same zone. Sign both views, configure internal resolvers to skip validation for that zone, or use a separate internal subdomain.

Conclusion

Split-horizon DNS solves a real, everyday problem: letting one hostname mean the right thing to each audience. Internal users get direct private paths and access to names that should never be public, while the outside world sees only what you choose to publish. The mechanics are simple — the server inspects where a query came from and picks a view — but the operational discipline matters more than the configuration syntax.

Most split-horizon outages come from the two views drifting apart, stale caches on roaming devices, or clients bypassing the internal resolver entirely. Keep your views generated from a single source, keep split records on short TTLs, push the right resolver to VPN and managed devices, and test both sides of the horizon after every change. Do that and split-horizon becomes an invisible, reliable part of your network.

Here are some useful references for going deeper on split-horizon DNS:

  1. ISC BIND 9 Documentation: BIND 9 Administrator Reference Manual — covers the view statement, match-clients, and ACL syntax.
  2. Microsoft Learn: Use DNS Policy for Split-Brain DNS Deployment — Microsoft's walkthrough of zone scopes and query resolution policies.
  3. AWS Documentation: Working with private hosted zones — how Route 53 private zones attach to VPCs and coexist with public zones.
  4. Unbound Documentation: unbound.conf reference — details on local-zone types and local-data.
  5. RFC 6762: Multicast DNS — explains why the .local name is reserved and should not be used for unicast internal zones.
Tags :
Share :

Related Posts

What Is the Difference Between Authoritative and Recursive DNS Servers?

What Is the Difference Between Authoritative and Recursive DNS Servers?

When someone says "the DNS server," they could mean two completely different machines doing two completely different jobs. One kind of server holds t

Continue Reading
Can DNS settings affect website speed?

Can DNS settings affect website speed?

Yes, DNS settings can significantly affect the speed at which a website loads for its users. DNS, or Domain Name System, is often likened to the inte

Continue Reading
Can You Use a CNAME Record on the Root Domain?

Can You Use a CNAME Record on the Root Domain?

It is one of the most common DNS questions there is. Your hosting platform says "add a CNAME pointing to myapp.example-cdn.net," it works perfectly

Continue Reading