A DNS zone transfer is how a secondary (replica) nameserver copies the full set of records for a zone from the primary (master) server so that the two stay in sync. It is the oldest replication mechanism in DNS, and it is still the backbone of multi-nameserver setups. There are two flavors: AXFR, the full transfer defined in RFC 5936, and IXFR, the incremental transfer defined in RFC 1995. Left open, zone transfers hand an attacker a complete map of your infrastructure; locked down with TSIG (RFC 8945), they are a safe, efficient way to run redundant authoritative servers. This guide covers how transfers work, how they are triggered, and how to secure them.
Primary, secondary, and why transfers exist
Authoritative DNS almost always runs on more than one server for resilience. Rather than editing the same zone file by hand on every machine, you edit it once on the primary and let the secondaries pull changes automatically. The secondary is configured with the primary's IP and the zone name; the rest is driven by the SOA record and a notify-then-transfer handshake.
The sequence looks like this:
- You edit the zone on the primary and bump the SOA serial number.
- The primary sends a NOTIFY message to each secondary telling it the zone changed.
- The secondary queries the primary's SOA and compares serials. If the primary serial is higher, the secondary is stale.
- The secondary requests a transfer, AXFR or IXFR, over TCP.
- The primary authorizes the request (by IP and/or TSIG) and streams the records.
If no NOTIFY arrives, the secondary still polls on the SOA refresh interval, so replication is self-healing even when a notify is lost.
AXFR vs IXFR
AXFR transfers the entire zone every time, which is simple and robust but wasteful for a large zone that changed by one record. IXFR transfers only the differences since the serial the secondary already holds, which is far lighter on the wire. A server that supports IXFR keeps a journal of recent changes; if it cannot satisfy an incremental request (for example the secondary is too far behind, or the journal was lost), it falls back to a full AXFR automatically.
| Aspect | AXFR | IXFR |
|---|---|---|
| RFC | 5936 | 1995 |
| Scope | Entire zone | Only changes since a serial |
| Transport | TCP | Prefers UDP, falls back to TCP |
| Server state | None required | Change journal required |
| Best for | First sync, recovery, small zones | Frequent small edits, large zones |
| Fallback | Not applicable | Falls back to AXFR when needed |
What an open transfer leaks
If a server answers AXFR to anyone, a stranger can download your entire zone in one command. That reveals every hostname, internal-looking subdomain, mail server, staging box, and VPN endpoint you have published. It is reconnaissance gold and a frequent first step in targeted attacks. Here is what an unauthorized transfer looks like when it succeeds, which it never should from the public internet:
# Attempt a full transfer against a nameserver
dig @ns1.example.com example.com AXFR
; Transfer failed when locked down:
; Transfer failed.
; What a wide-open server would return (BAD):
example.com. 3600 IN SOA ns1.example.com. host.example.com. 2026101401 7200 3600 1209600 3600
example.com. 3600 IN NS ns1.example.com.
www.example.com. 3600 IN A 203.0.113.10
vpn.example.com. 3600 IN A 203.0.113.40
staging.example.com. 3600 IN A 10.20.0.5
Locking transfers down with TSIG
The weak way to restrict transfers is an IP allow list. It helps, but source IPs can be spoofed and secondaries behind NAT or dynamic addressing break it. The strong way is TSIG, a transaction signature that adds an HMAC computed over the message using a shared secret. The primary signs its answer and the secondary verifies it (and vice versa for the request), so authentication does not depend on the source address at all.
First generate a key, then reference it in both servers. Using BIND tooling:
# Generate a shared TSIG key (HMAC-SHA256)
tsig-keygen -a hmac-sha256 xfr-key > xfr.key
cat xfr.key
key "xfr-key" {
algorithm hmac-sha256;
secret "aZ3k...base64...==";
};
On the primary, require the key for transfers and keep an IP guard as defense in depth:
// named.conf on the PRIMARY
include "/etc/bind/xfr.key";
zone "example.com" {
type primary;
file "/etc/bind/db.example.com";
allow-transfer { key "xfr-key"; }; // TSIG required
also-notify { 203.0.113.53; };
notify yes;
};
On the secondary, present the same key when contacting the primary:
// named.conf on the SECONDARY
include "/etc/bind/xfr.key";
server 203.0.113.1 { keys { "xfr-key"; }; };
zone "example.com" {
type secondary;
file "/var/cache/bind/db.example.com";
primaries { 203.0.113.1; };
};
Verify by running a signed transfer yourself; it should succeed with the key and fail without it:
# Should succeed, signed with the shared key
dig @203.0.113.1 example.com AXFR -y hmac-sha256:xfr-key:aZ3k...base64...==
# Same request with no key should be refused
dig @203.0.113.1 example.com AXFR
; Transfer failed.
TSIG operational notes
- Clock sync matters. TSIG signatures carry a timestamp with a default fudge window of 300 seconds. If the primary and secondary clocks drift apart, signatures are rejected with BADTIME. Run NTP on both.
- Protect the secret. The key file grants transfer rights. Restrict its permissions and never commit it to a public repo.
- Rotate keys. Treat the shared secret like any credential and rotate it on a schedule, updating both ends in a brief overlap window.
- Prefer modern algorithms. Use hmac-sha256 rather than the legacy hmac-md5.
Common failure modes
| Symptom | Likely cause | Fix |
|---|---|---|
| Transfer failed, REFUSED | allow-transfer excludes the secondary or key mismatch | Confirm key name and secret match on both ends |
| BADTIME / BADSIG | Clock drift or wrong secret | Sync NTP, re-copy the secret |
| Secondary never updates | SOA serial not incremented or NOTIFY blocked | Bump serial, allow NOTIFY, check firewall on TCP 53 |
| Always does AXFR, never IXFR | Journal missing or server lacks IXFR support | Enable journaling and incremental transfers |
Firewall and port notes
Zone transfers use TCP because they can exceed the size of a single UDP packet. Make sure TCP port 53 is open between primary and secondary in both directions needed for NOTIFY and transfer. Blocking TCP 53 entirely is a classic reason replication silently stalls while ordinary lookups keep working over UDP.
Once your secondaries are syncing cleanly, confirm that the records actually appear consistent from resolvers worldwide using the Valla DNS propagation checker, and browse the Valla DNS learning hub for companion guides on SOA tuning, TTL strategy, and DNSSEC, all of which interact with how and when your transfers fire.