DNS (RFC 1035, 1987) opens every message with the same fixed 12-byte header regardless of whether it's a query or a response - one ID field and two packed flag bytes tell a resolver everything it needs before it even looks at the question. Everything after the header - questions, answers, authority, additional records - is built from one repeating record shape, with a compression scheme to keep repeated names from bloating a message that, for decades, had to fit in a single 512-byte UDP datagram. Hover a field to light its bytes, click a field marked + for its lookup table.
messagestandard query, A recordspecRFC 1035 §4.1.1
A resolver asking for example.com's A record, recursion desired. Every DNS message - query or response, over UDP or TCP - opens with exactly this 12-byte shape.
header, ID through ARCOUNT (12 bytes)
byte 2QR/Opcode/AA/TC/RD - unchanged since 1987byte 3RA/Z/RCODE, reallocated in 1999
The first flags byte hasn't moved in almost 40 years. The second byte's 3-bit "must be zero" reserved field is where DNSSEC later carved out two new bits without touching the wire format at all.
byte 2: QR, Opcode, AA, TC, RD
byte 3 (RFC 1035, 1987): RA, Z, RCODE
byte 3 (RFC 2535, 1999): RA, Z, AD, CD, RCODE
same bits, new meaning. RFC 2535 (1999) took two of the three reserved "Z" bits from RFC 1035 and assigned them to AD (Authentic Data - the resolver validated DNSSEC signatures on this answer) and CD (Checking Disabled - please skip validation). No message got longer; a "must be zero, ignored" field just stopped being ignorable, the same way TCP's reserved bits later became ECN's CWR/ECE.
No length field for the whole name and no separator bytes - each label is prefixed by its own length, and the name ends at a zero-length label (the DNS root). "example.com" is really two labels: 7-byte "example", then 3-byte "com".
QNAME + QTYPE + QCLASS (17 bytes)
why length-prefixed instead of null-terminated. A length byte can never be confused with a delimiter, which matters because DNS labels aren't restricted to printable ASCII - RFC 1035 only requires a label to be 1-63 bytes. The top two bits of that length byte are also how the compression scheme in the Answer tab tells "this is a label" (00) apart from "this is a pointer" (11), so no ordinary label can ever be misread as one.
name0xC00C, a compression pointersize16 bytes
Instead of repeating "example.com" again, this answer's NAME field points back at byte offset 12 - exactly where the QNAME started in the Question tab - and reuses it verbatim.
answer resource record for example.com A (16 bytes)
how the pointer is spotted. A 2-byte field whose top two bits are both 1 is a pointer, not a label length - RFC 1035 caps ordinary label lengths at 63 (0b00111111), so a length byte can never legally set both top bits. The remaining 14 bits (0x000C = 12) are a byte offset from the start of the DNS message, which is also why compression can only point backward, never forward, into a message.
added1999, RFC 2671 → RFC 6891size11 bytes
EDNS0 doesn't change the 12-byte header at all - it's a pseudo resource record dropped into the additional section, repurposing an RR's CLASS and TTL fields to carry a larger UDP payload size and extra header bits instead of the class/lifetime they'd normally mean.
OPT pseudo-RR in the additional section (11 bytes)
why this exists. RFC 1035 UDP responses were capped at 512 bytes, forcing a retry over TCP for anything bigger - increasingly common once DNSSEC signatures started riding in responses. EDNS0's CLASS field advertises how large a UDP response the sender can actually receive (4096 bytes here), and its repurposed TTL field carries the DO bit that says "please include DNSSEC signatures."
The 12-byte header and the label/RR shapes above have outlived every one of these revisions - each addition either reused reserved bits or rode along as an additional pseudo-record, never forcing a wire-format break.
era
addition
how it fit without breaking the header
1987
RFC 1035 - names, header, resource records
the baseline this whole page diagrams
1996
RFC 1996 - NOTIFY, for zone-transfer triggering
new opcode value (4) in the existing 4-bit Opcode field
1999
RFC 2535 - DNSSEC signatures; RFC 2671 - EDNS0
AD/CD claimed from the reserved Z bits; EDNS0 added as an OPT pseudo-RR, see the EDNS0 tab
new RR types only - no header or flag changes needed
2016
RFC 7858 - DNS over TLS (DoT), port 853
same message format, just a TLS-wrapped TCP socket instead of plain UDP/TCP 53
2018
RFC 8484 - DNS over HTTPS (DoH)
same message format again, carried as the body of an HTTP request/response
the pattern. Almost forty years in, nobody has ever had to add a 13th byte to the header or change what QR/Opcode/RD mean. Transport got wrapped (DoT, DoH), the message got bigger records bolted on (DNSSEC, EDNS0), but RFC 1035's core shape is still exactly what's on the wire.