Networking ยท DNS ยท TLS
๐Ÿ”
Name โ†’ Session
DNS and TLS emblem

DNS and TLS Architecture

A browser never opens a TCP socket to a brand name. It opens a socket to an address it learned from a resolver, then it authenticates that address with a certificate chain. Miss either hop and the product is down, even when every application process is healthy.

Barnabas Waweru 26 August 2026 18 min read
ansi ยท wordmark ยท dns tls
โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ•—   โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—  โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•—     โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—
โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ•—  โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ•โ•โ•โ•โ•  โ•šโ•โ•โ–ˆโ–ˆโ•”โ•โ•โ•โ–ˆโ–ˆโ•‘     โ–ˆโ–ˆโ•”โ•โ•โ•โ•โ•
โ–ˆโ–ˆโ•‘  โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—     โ–ˆโ–ˆโ•‘   โ–ˆโ–ˆโ•‘     โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—
โ–ˆโ–ˆโ•‘  โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•‘โ•šโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•‘โ•šโ•โ•โ•โ•โ–ˆโ–ˆโ•‘     โ–ˆโ–ˆโ•‘   โ–ˆโ–ˆโ•‘     โ•šโ•โ•โ•โ•โ–ˆโ–ˆโ•‘
โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ•‘ โ•šโ–ˆโ–ˆโ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•‘     โ–ˆโ–ˆโ•‘   โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•‘
โ•šโ•โ•โ•โ•โ•โ• โ•šโ•โ•  โ•šโ•โ•โ•โ•โ•šโ•โ•โ•โ•โ•โ•โ•     โ•šโ•โ•   โ•šโ•โ•โ•โ•โ•โ•โ•โ•šโ•โ•โ•โ•โ•โ•โ•

Two Protocols, One User Path

The job split

DNS answers "which address, which service parameters." TLS answers "is this the name I asked for, and can we speak without eavesdroppers." They are independent protocols. Production treats them as one path because a client cannot start TLS until it has a destination, and a correct destination is useless if the handshake fails.

The application only sees a hostname. The stack underneath walks: stub resolver โ†’ recursive resolver โ†’ authoritative servers โ†’ cached answer โ†’ TCP or QUIC connect โ†’ TLS handshake โ†’ application bytes.

Recursive DNS resolver as a dark-glass telescope aimed at layered name-server moons
Recursive resolution is a telescope, not a phone book. The stub asks one resolver. That resolver walks the hierarchy and caches what it learns.
schema ยท client path
CLIENT
  stub resolver (OS / libc / getaddrinfo)
       โ”‚  UDP/53 or DoT:853 or DoH:443
       โ–ผ
RECURSIVE RESOLVER  (ISP, 1.1.1.1, 8.8.8.8, corporate)
  cache? โ†’ return
  else walk:
    root  โ†’ TLD  โ†’ authoritative
       โ”‚
       โ–ผ
ANSWER: A / AAAA / CNAME / HTTPS / SVCB  + TTL
       โ”‚
       โ–ผ
TRANSPORT  TCP:443 or QUIC:443
       โ”‚
       โ–ผ
TLS 1.3  ClientHello (SNI) โ†’ ServerHello โ†’ EncryptedExtensions
         Certificate + CertificateVerify โ†’ Finished
       โ”‚
       โ–ผ
APPLICATION  HTTP/2 or HTTP/3
The production invariant

If DNS or TLS is wrong, your deploy is irrelevant. Health checks that only ping the origin miss both layers. Probe the name from the public resolver set your users actually use, then complete a TLS handshake with the same SNI the browser sends.

Recursive Resolution

Stub, recursive, authoritative

The stub on the device is deliberately thin. It forwards a query and waits. The recursive resolver does the work: it starts at a root hint, follows NS delegations, and caches every useful RRset it sees. Authoritative servers own a zone. They do not recurse.

Mixing those roles is how you get loops and cache poisoning surfaces. A nameserver that is authoritative for example.com should not also recurse for random clients on the same socket.

1
Stub query
Application calls getaddrinfo("app.example.com"). Stub sends QTYPE A and AAAA, often in parallel.
2
Cache check
Recursive resolver returns a cached RRset if TTL remains. Negative answers are cached too (NXDOMAIN / NODATA).
3
Hierarchy walk
Root hints โ†’ gTLD or ccTLD โ†’ zone apex NS. Each hop is a referral, not the final answer.
4
Authoritative answer
The zone's NS returns the RRset plus TTL. Resolver caches and replies to the stub.
mermaid ยท flowchart
flowchart TD
  A[Stub resolver] -->|QNAME QTYPE| B{Recursive cache}
  B -->|HIT TTL live| C[Return RRset]
  B -->|MISS| D[Query root]
  D --> E[TLD NS referral]
  E --> F[Authoritative NS]
  F -->|A AAAA HTTPS| B
  C --> G[Client connect]
  F -->|NXDOMAIN| H[Negative cache]

TTLs are a contract, not a knob you turn once

A 30-second A record sounds agile until every recursive resolver on earth still holds the previous answer. A 24-hour TTL sounds cheap until you need to fail away from a dead anycast. Pick TTLs per record class: long for NS and MX that almost never move, short for A/AAAA that sit in front of a failover, longer for CAA that you rotate on a calendar.

Resolvers may clamp TTLs. Some recursive operators raise very low TTLs. Do not design a cutover that requires every cache on the internet to honor a 5-second record.

The Record Set That Actually Matters

A / AAAA
IPv4 and IPv6 destinations. Publish both or you silently drop a growing share of mobile clients. Happy Eyeballs races them.
CNAME
Alias. Illegal at the zone apex. CDN and SaaS frontends live here. Chains add latency and failure modes.
NS / SOA
Delegation and zone metadata. SOA serial, refresh, retry, expire, and minimum (negative TTL) are operational, not decoration.
MX / TXT
Mail routing and policy (SPF, DKIM, DMARC, domain verification). TXT is a junk drawer. Treat it as production data.
CAA
Which certificate authorities may issue for the name. An empty CAA set is "anyone." That is a choice. Make it explicit.
HTTPS / SVCB
Service binding (RFC 9460 / 9461). Advertises ALPN, port, and alternate endpoints before the TLS handshake. This is how a client learns HTTP/3.
zone ยท BIND-style excerpt
example.com.        3600 IN SOA  ns1.example.com. hostmaster.example.com. (
                              2026082601 7200 3600 1209600 300 )
example.com.        86400 IN NS   ns1.example.com.
example.com.        86400 IN NS   ns2.example.com.
example.com.        3600  IN CAA  0 issue "letsencrypt.org"
example.com.        3600  IN CAA  0 issuewild ";"
www.example.com.    60    IN A    203.0.113.10
www.example.com.    60    IN AAAA 2001:db8::10
www.example.com.    300   IN HTTPS 1 . alpn="h3,h2"
Apex CNAME is still illegal

You cannot put a CNAME at the apex next to SOA and NS. Providers fake it with ANAME, ALIAS, or flattening: they resolve the target server-side and serve A/AAAA. Know which you have. Flattening hides the target TTL and can delay failovers by whatever interval the provider re-resolves.

DNSSEC: Authenticity, Not Confidentiality

What it proves

DNSSEC signs RRsets so a validating resolver can detect tampering. It does not encrypt the query. A passive observer on UDP/53 still sees QNAMEs. Confidentiality is DoT or DoH. Authenticity is DNSSEC. They solve different attacks.

The chain is: DNSKEY at the zone, RRSIG over each signed RRset, DS at the parent that pins the child key. A break in that chain is indistinguishable from "unsigned" to a validator that requires a secure path. Half-deployed DNSSEC is worse than none: a broken DS takes the name off the internet for validating resolvers.

Property Plain DNS DNSSEC DoT / DoH
Integrity of answer None (spoofable) Signed RRsets Channel integrity only
Query privacy Cleartext Cleartext Encrypted to the resolver
Failure mode Wrong answer possible SERVFAIL on bad chain Timeout if resolver unreachable

DoT and DoH

Same records, different pipe

DNS over TLS (RFC 7858) wraps the DNS message in TLS on port 853. DNS over HTTPS (RFC 8484) carries it as an HTTP exchange on 443, usually application/dns-message. Both hide QNAMEs from the local network. Neither hides them from the recursive operator you chose.

Enterprise networks that intercept UDP/53 lose visibility when the stub speaks DoH to a public resolver. That is the policy fight. Architecturally: pick one recursive trust anchor per environment (device, office, CI) and pin it. Split-horizon DNS and DoH to a public resolver will leak internal names or return the wrong public answer.

Resolver choice is a trust decision

Whoever recurses for you sees every name the fleet resolves: staging hosts, partner admin panels, internal package registries if they leak. Treat the recursive resolver like a log sink. Do not point production stubs at a random public IP because a tutorial did.

TLS 1.3 Handshake

Two dark-glass satellites exchanging a braided TLS handshake
TLS 1.3 encrypts most of the handshake. The cleartext leftover that still matters is the ClientHello, including SNI unless you deploy ECH.

What still happens in the clear

RFC 8446 cut a round trip and hid certificates from the path. The ClientHello is still visible: protocol version, cipher suites, key share, ALPN, and Server Name Indication. A middlebox that wants to route or filter on hostname still can, unless Encrypted Client Hello is negotiated end to end.

TLS 1.2 remains on old stacks. New work should be 1.3 only. 1.0 and 1.1 are dead. If a vendor requires 1.0, that is a vendor problem, not a reason to reopen the protocol on your edge.

mermaid ยท sequence
sequenceDiagram
  participant C as Client
  participant S as Server
  C->>S: ClientHello (SNI, key_share, ALPN)
  S->>C: ServerHello (key_share)
  S->>C: EncryptedExtensions
  S->>C: Certificate + CertificateVerify
  S->>C: Finished
  C->>S: Finished
  C->>S: Application data (HTTP)
SNI
Tells a shared IP which certificate to present. Wrong SNI yields the default cert and a name mismatch. Multi-tenant edges live or die on this field.
ALPN
Negotiates h2 vs h3 vs http/1.1 before the first request. Misconfigured ALPN is why a client falls back to HTTP/1.1 on a "modern" edge.
Session resumption
PSK tickets skip a full handshake. Rotate ticket keys. A stolen ticket key forges resumptions for the ticket lifetime.
0-RTT
TLS 1.3 early data is replayable. Safe for idempotent GETs. Dangerous for anything that charges a card or mutates state.
nginx ยท TLS 1.3 edge
ssl_protocols TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_certificate     /etc/ssl/example.fullchain.pem;
ssl_certificate_key /etc/ssl/example.key;
ssl_stapling on;
ssl_stapling_verify on;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

Certificates and Automation

The chain the client actually walks

Leaf certificate names the host (SAN, not CN in modern clients). Intermediate is signed by a root in the client's trust store. Serve leaf plus intermediates. Do not serve the root. Missing intermediates are the "works on my laptop, fails on mobile" special: the laptop cached the intermediate from another site.

ACME (RFC 8555) is how Let's Encrypt and other CAs issue without a human in the loop. HTTP-01 proves control of port 80. DNS-01 proves control of a TXT record and is the only sane path for wildcards and locked-down origins.

1
Account + order
ACME client creates an order for the SAN list. CA returns challenges.
2
Prove control
HTTP-01 file or DNS-01 TXT. CAA must allow this CA or issuance fails.
3
Issue + install
Client writes leaf + chain + key. Reloads the edge. Monitors notAfter, not the calendar on someone's desk.
4
Revoke + rotate
Compromise or name retirement: revoke, replace, and treat OCSP/CRL as best-effort. Short-lived certs shrink the window.
Expiry is still the outage

Most TLS outages are not novel cryptanalysis. They are a leaf that expired because the ACME hook could not write the zone, the reload never ran, or the monitor checked the origin certificate while users hit a different edge cert. Alert on notAfter from the public name, from the same vantage as your users.

Failure Modes That Page People

The ones that look like "the app is down"

  • NXDOMAIN after a registrar lapse. The zone is gone. Application metrics stay green until clients stop arriving.
  • Stale A after a cutover. TTL plus flattening delay. Half the world is on the new VIP, half is not.
  • Broken DS after a DNSSEC key roll. Validating resolvers SERVFAIL. Non-validating resolvers work. You will argue with yourself for an hour.
  • Wrong SNI on a shared VIP. Certificate name mismatch. Browsers show a block page. curl -k hides it in CI.
  • Expired leaf or incomplete chain. Desktop browsers sometimes forgive a cached intermediate. Mobile does not.
  • CAA that forbids your CA. ACME starts failing days before expiry. If you only alert on expiry, you learn at the weekend.
Symptom Look at first Do not look at first
Users cannot resolve the name Public recursive, NS, SOA, registrar Application pods
Name resolves, handshake fails SNI, chain, notAfter, cipher, ALPN HTTP handler code
Some networks work, others fail Split horizon, DoH policy, IPv6, MTU A single region deploy

Key Takeaways

Principles

  1. DNS and TLS are one user path. Probe both from public vantage points, not from inside the VPC.
  2. Keep stub, recursive, and authoritative roles separate. Recursion on an authoritative socket is a footgun.
  3. TTLs are a failover budget. Short where you cut over. Long where you do not. Flattening hides the real budget.
  4. Publish A and AAAA together. Happy Eyeballs will not save a missing record.
  5. DNSSEC authenticates. DoT/DoH conceals. Deploy them for the attack you actually have.
  6. TLS 1.3, full chain, correct SNI. 0-RTT is not for mutations.
  7. Automate issuance and watch public notAfter. CAA and ACME failures precede expiry.
  8. HTTPS/SVCB is now part of the name. ALPN and HTTP/3 are DNS problems as much as edge problems.

Official Documentation