How DNS and TLS Actually Work: A Developer's Guide (2026)

Written By  Crosscheck Team

Content Team

May 22, 2026 13 minutes

How DNS and TLS Actually Work: A Developer's Guide (2026)

What Happens Between Typing a URL and the Page Loading

DNS turns a hostname like crosscheck.app into an IP address. TLS encrypts the connection to that IP before any HTTP bytes flow. Together they cover the first two seconds of every page load — the resolver chain on the DNS side, the handshake on the TLS side. Most developers use both daily and couldn't sketch either on a whiteboard. This guide walks the whole journey end to end, with dig and openssl s_client examples and the error codes you've definitely seen.

Key takeaways

  • DNS resolution walks a chain — browser cache, OS cache, recursive resolver, root, TLD, authoritative — each layer with its own TTL.
  • TLS 1.3 supports roughly 73–75% of top sites as of mid-2025 (SSL Pulse, June 2025); TLS 1.2 still serves the long tail of legacy clients.
  • TLS 1.3 is 1-RTT by default and 0-RTT for resumed sessions, against 2-RTT for TLS 1.2.
  • Most "site won't load" errors map to one layer — DNS (ERR_NAME_NOT_RESOLVED) or TLS (ERR_CERT_*).
  • DoH is default-on in Chrome, Firefox, and Edge. ECH (RFC 9849, March 2026) closes the last SNI leak.

The full journey, in 12 steps

Press Enter and the browser sets off a chain that, on a good day, finishes in 200–500 ms:

  1. Parse the URL into scheme, host, port, path.
  2. Check the HSTS list — if the host is on it, upgrade http:// to https:// immediately.
  3. Look up the IP — browser cache → OS resolver cache → configured DNS resolver → recursive walk.
  4. Open a TCP connection on port 443 (or QUIC over UDP for HTTP/3).
  5. TLS ClientHello — versions and ciphers the client supports.
  6. TLS ServerHello — server picks a cipher, sends its certificate chain.
  7. Client verifies the chain against its trusted root store.
  8. Both sides derive session keys; encrypted application data starts flowing.
  9. HTTP request → response → render. Every cross-origin asset triggers more DNS + TLS rounds.

Steps 3 through 8 are DNS and TLS — and they're where most "the site is slow" or "the site won't load" investigations actually live.


DNS: how a name becomes an IP

DNS is a hierarchical, distributed database. The mental model is a tree. The root zone (.) sits at the top. Below it sit the TLDs.com, .app, .io, .co.uk. Below each TLD sit the authoritative nameservers for individual domains, which hold the actual records (A, AAAA, CNAME, MX, TXT, NS) that map hostnames to IPs and other metadata.

The browser does not walk this tree itself. It hands the question to a recursive resolver — typically your ISP's resolver, or a public one like 1.1.1.1 (Cloudflare), 8.8.8.8 (Google), or 9.9.9.9 (Quad9). The recursive resolver does the walking.

Recursive vs authoritative resolvers

Resolver typeWhat it does
Recursive resolverReceives a query, walks the tree on your behalf, caches the answer (Cloudflare 1.1.1.1, Google 8.8.8.8, your ISP)
Root nameserverTells the recursive resolver which TLD nameserver to ask
TLD nameserverOwns each TLD — Verisign for .com, Public Interest Registry for .org
Authoritative nameserverHolds the actual DNS records for the domain and answers definitively

A clean lookup of crosscheck.app: recursive resolver asks the root, root says "ask the .app nameserver", .app nameserver says "ask the authoritative nameserver for crosscheck.app", authoritative nameserver returns the A record. The recursive resolver caches that answer for the TTL the authoritative server specified, then hands it back to the OS.

Record types you actually use

RecordWhat it points toUse case
A / AAAAIPv4 / IPv6 addressMap crosscheck.app to an IP
CNAMEAnother hostnameAlias www.crosscheck.app to crosscheck.app
MXMail server hostname + priorityRoute email to Google Workspace, Fastmail, etc.
TXTArbitrary textSPF, DKIM, DMARC, ownership verification
NSAuthoritative nameservers for the zoneDelegating a subdomain or moving DNS providers
HTTPS / SVCBService binding with ALPN, port, ECH configHTTP/3 advertisement, ECH key delivery

The newer HTTPS record (RFC 9460) is the one to watch — it's how browsers learn about HTTP/3 endpoints and ECH keys in a single DNS lookup, instead of starting a TCP connection and then upgrading.

TTL and caching layers

Every DNS record has a TTL — time-to-live in seconds — that tells every caching layer how long it may reuse the answer. A typical web record sits at 300–3600. Lower it before a migration so you can flip records quickly; raise it back after.

DNS gets cached in at least four places:

  • Browser cache — Chrome holds DNS answers for around 60 seconds; check chrome://net-internals/#dns.
  • OS resolver cache — macOS mDNSResponder, Linux systemd-resolved, Windows DNS Client. Flush with sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder on macOS or ipconfig /flushdns on Windows.
  • Router / ISP recursive resolver — caches according to the record's TTL. This is usually the layer that's stale when your DNS change "hasn't propagated yet".
  • Authoritative nameserver — the source of truth.

The "DNS propagation can take 48 hours" line is mostly about these caches stacking. The authoritative record updates instantly; downstream resolvers update when their TTLs expire.

Inspecting DNS with dig

dig is the tool. Skip nslookup — it's older and gives you less detail. Basic shape:

dig crosscheck.app
dig @1.1.1.1 crosscheck.app +trace   # walk the tree visibly
dig crosscheck.app MX                # mail records
dig crosscheck.app TXT               # SPF / DKIM / DMARC
dig crosscheck.app HTTPS             # HTTPS / SVCB record (ECH, HTTP/3)
dig +short crosscheck.app            # just the IP

+trace is the single most useful flag when debugging propagation — it shows root → TLD → authoritative explicitly. If the answer is NXDOMAIN, the domain doesn't exist. If it's SERVFAIL, the authoritative nameserver is broken or unreachable. If dig returns the IP but the browser still says ERR_NAME_NOT_RESOLVED, the browser is probably using DoH against a resolver that disagrees with your shell's resolver — open chrome://net-internals/#dns to confirm.


DNS over HTTPS and DNS over TLS

Traditional DNS sends queries in plaintext over UDP port 53. Your ISP sees every hostname you look up — and so does anyone sniffing your café Wi-Fi. DoH (RFC 8484) wraps DNS queries in HTTPS to a DoH-enabled resolver. DoT (RFC 7858) wraps them in a dedicated TLS connection on port 853. Same goal, different transport.

In practice, DoH is the one developers see. Firefox shipped it on by default in the US in February 2020; Chrome followed in May 2020. As of 2026 both browsers ship DoH-on-by-default in most regions when the configured resolver supports it, and Firefox reports DoH adoption above 85% among US users. Chrome ships five pre-configured providers — Google, Cloudflare, Quad9, NextDNS, CleanBrowsing — and will auto-upgrade if your system resolver matches one.

Three things change for developers:

  • DNS queries don't show up in tcpdump on port 53 — they show up as HTTPS to your resolver. Expect encrypted HTTPS to 1.1.1.1 or dns.google, not plaintext UDP.
  • Corporate split-horizon DNS can break. Browser DoH bypasses the system resolver, which can route internal lookups to a public DoH resolver that doesn't know your internal zones. The fix is usually an MDM policy disabling DoH or a corporate canary domain.
  • DoH is a hard prerequisite for ECH — ECH keys are fetched via DNS, and you don't want that record going out in plaintext.

TLS: how the connection becomes private

Once the browser has an IP, it opens a TCP connection on port 443 and starts a TLS handshake. The handshake has two jobs — agree on a cipher suite, and verify the server is who the certificate says it is.

TLS 1.2 was published in 2008 and remains universally supported. TLS 1.3 was finalised in 2018 (RFC 8446) and is now the dominant protocol on the public web — roughly 73% of encrypted traffic and 75.3% of top sites as of June 2025 per Qualys SSL Pulse. TLS 1.2 still hangs around in older enterprise stacks, embedded devices, and middleboxes. All major browsers, CDNs, and load balancers support both.

The TLS 1.3 handshake, step by step

The TLS 1.3 handshake is 1-RTT — one round trip — which is the single biggest reason it's faster than TLS 1.2's 2-RTT handshake.

  1. ClientHello — client sends supported TLS versions, cipher suites, key exchange groups, the SNI, and — critically for 1.3 — a key share (its public ECDHE key for one or more groups it expects the server to accept).
  2. ServerHello — server picks a cipher and key exchange group, sends its own key share. From that moment both sides can derive session keys, and the rest of the handshake is encrypted.
  3. EncryptedExtensions, Certificate, CertificateVerify, Finished — sent in the same flight as ServerHello. The certificate proves the server owns the hostname; CertificateVerify proves the server holds the matching private key; Finished commits both sides to the keys.
  4. Client Finished — client verifies the chain, sends its own Finished, and application data follows in the same flight.

One round trip — ClientHello out, ServerHello + Finished back. TLS 1.2 needed two because the key exchange parameters weren't in the ClientHello.

0-RTT and session resumption

TLS 1.3 also defines 0-RTT (early data). If the client has a recent session ticket from a previous connection, it can send application data in the very first flight — alongside ClientHello — encrypted under a key derived from the previous session. Page loads from a known origin can feel near-instant.

The catch: 0-RTT data is replayable. An attacker who captured the encrypted early data can replay it; the server has no way to tell. So 0-RTT is safe for idempotent GETs and dangerous for anything that mutates state. CDNs like Cloudflare and Fastly only allow 0-RTT for GETs, and you should too.

TLS 1.2 vs TLS 1.3 at a glance

AspectTLS 1.2TLS 1.3
Handshake RTT21 (0 with resumption)
Cipher suitesMany — including RSA, CBC, RC4Five AEAD-only suites
Forward secrecyOptionalMandatory
Encrypted handshake messagesNoYes (after ServerHello)
Adoption among top sites100% support~75.3% support (June 2025)

If you're still terminating TLS on infrastructure that doesn't support 1.3, the upgrade is almost always worth it — faster handshakes, smaller attack surface, forward secrecy by default.


Certificates, chains, and the trust store

The certificate the server sends is a signed claim — "this public key belongs to crosscheck.app" — signed by a Certificate Authority (CA). The CA's certificate is itself signed by a higher CA, up to a root certificate that's pre-installed in the OS or browser trust store.

A typical chain has three or four certs:

  • Leaf certificate — for crosscheck.app, valid 90 days (Let's Encrypt) or up to ~13 months for paid CAs.
  • Intermediate certificate(s) — signed the leaf, owned by the CA.
  • Root certificate — the trust anchor, in your local store. The server does not send this; your client already has it.

The client walks the chain: leaf → intermediate → root. If every signature checks out and the root is in the local trust store, the cert is trusted. If anything fails — broken signature, expired cert, missing intermediate, unknown root — the handshake aborts and the browser shows an error.

Three things worth knowing:

  • Trust stores are not one shared list. macOS uses Apple's, Windows uses Microsoft's, Mozilla maintains its own — each runs its own CA/Browser Forum compliance process and can distrust a CA unilaterally.
  • Let's Encrypt certs last 90 days; automated renewal is non-negotiable.
  • If you pin certificates, pin the leaf's public key or the root — never the intermediate.

Inspecting TLS with openssl s_client

The TLS equivalent of dig is openssl s_client. It connects to a host and prints the negotiated parameters and the full certificate chain:

# Full handshake + chain (note: -servername sets the SNI)
openssl s_client -connect crosscheck.app:443 -servername crosscheck.app

# Just the expiry dates
echo | openssl s_client -connect crosscheck.app:443 -servername crosscheck.app 2>/dev/null \
  | openssl x509 -noout -dates

# Force a specific TLS version (useful for legacy debugging)
openssl s_client -connect crosscheck.app:443 -servername crosscheck.app -tls1_2
openssl s_client -connect crosscheck.app:443 -servername crosscheck.app -tls1_3

-servername matters — without it, a virtual-hosted server won't know which cert to send. If the server only supports TLS 1.3 and the client forces 1.2, the handshake fails with no protocols available — exactly what you want to see in that test.


Encrypted Client Hello (ECH)

There's one leak left in TLS 1.3 — the ClientHello includes the SNI in plaintext, the hostname the client wants to talk to. A network observer can see crosscheck.app even though they can't see the page contents. For shared-IP hosting (most CDNs), this is the last metadata leak in HTTPS.

ECH closes it. The client encrypts the inner ClientHello — including the SNI — using a public key fetched from the server's DNS HTTPS record. The outer ClientHello shows a generic hostname (typically the CDN's public name). The observer sees the client talking to "the CDN" but not which tenant.

ECH was finalised as RFC 9849 in March 2026 (OpenSSL announcement), alongside RFC 9848 for DNS bootstrapping. Firefox enabled it by default starting in version 119; Chrome is rolling out gradually on stable; Safari supports it on recent Apple OS releases; Edge ships it as policy-controllable. Two prerequisites: DoH (or DoT) must be active, and the server must publish an HTTPS record with an ech= parameter. Check with dig crosscheck.app HTTPS — if you see ech=<base64>, the server is advertising a config.


Common browser errors and what they really mean

Most "can't load the site" errors map to a specific step in the DNS or TLS flow. The big ones:

Chrome errorLayerWhat it meansFirst thing to check
ERR_NAME_NOT_RESOLVEDDNSHostname did not resolveTypo in URL, dead domain, broken local DNS, DoH disagreement
ERR_CONNECTION_REFUSEDTCPServer refused on the portService not running, wrong port, firewall
ERR_CONNECTION_TIMED_OUTTCPNothing answeredWrong IP, packet filter, server down
ERR_SSL_PROTOCOL_ERRORTLS handshakeNegotiation failed before certsVersion mismatch, broken middlebox, no shared cipher
ERR_CERT_AUTHORITY_INVALIDTLS chainCert signed by an untrusted CASelf-signed cert, missing intermediate, corporate HTTPS inspection
ERR_CERT_DATE_INVALIDTLS datesCert expired or device clock wrongopenssl s_client for not-after, then local clock
ERR_CERT_COMMON_NAME_INVALIDTLS hostnameHostname doesn't match cert's SAN listCert issued for the wrong domain or missing the SAN
ERR_CERT_REVOKEDTLS revocationCA revoked the cert via CRL or OCSPReissue and redeploy

Two error patterns trip up everyone at some point:

  • A cert chain that works in Chrome but fails on iOS or curl — almost always a missing intermediate. Chrome's AIA fetching can pull a missing intermediate from the leaf certificate; many other clients won't. Always serve the full chain.
  • ERR_CERT_AUTHORITY_INVALID on a corporate laptop only — your employer's HTTPS-inspecting proxy (Zscaler, Palo Alto, Netskope) is terminating TLS and re-signing with its own CA. That CA is normally installed by MDM; if the install failed, every HTTPS site looks broken.

A debugging cheatsheet

When a site won't load, walk the layers in order:

# 1. Is DNS working?
dig +short example.com
dig @1.1.1.1 example.com +trace

# 2. Can TCP reach the port?
nc -vz example.com 443

# 3. Does TLS complete?
openssl s_client -connect example.com:443 -servername example.com -brief

# 4. Is the cert valid for this name and not expired?
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates

# 5. Does HTTPS round-trip a request?
curl -v https://example.com/

# 6. What does the browser see?
# Open chrome://net-internals/#dns and chrome://net-internals/#sockets

If steps 1–4 pass and the browser still complains, the issue is browser-level — extension interfering, stale HSTS pin, cached DoH answer, certificate transparency requirement. The chrome://net-internals panels are the source of truth there. For the broader bug-isolation flow, the step-by-step debugging guide covers how to identify which layer owns any given bug, and javascript debugging tips for developers handles errors that surface after the page loads.


FAQ

Why is DNS resolution sometimes slow even when my connection is fast?

DNS speed depends on the resolver, not the bandwidth. A cold lookup against a slow ISP resolver can take 100–300 ms; a warm lookup against 1.1.1.1 is usually under 10 ms. Switch resolvers if your default is consistently slow.

Why does my DNS change take so long to propagate?

The authoritative record changes instantly. What takes time is every downstream cache hitting the end of its TTL. Lower your TTL to 60–300 seconds a day before any planned change, then raise it again after.

Is TLS 1.2 still safe to use in 2026?

Yes, if it's configured correctly — TLS 1.2 with AEAD cipher suites (ECDHE + AES-GCM or ChaCha20-Poly1305) and forward secrecy is still considered secure. What's not safe is TLS 1.2 with legacy ciphers like RC4, 3DES, RSA key exchange, or CBC modes.

What's the difference between SSL and TLS?

SSL is the old name; TLS is the current name. SSL 2.0 and 3.0 were retired (SSL 3.0 was killed by POODLE in 2014); the protocol was renamed TLS at version 1.0 in 1999. People still say "SSL certificate" out of habit, but everyone means TLS.

Why does my cert work in Chrome but not in my Node.js script?

Node uses its own bundled CA list, not the system trust store. If you have a corporate CA installed at the OS level, Node won't see it unless you set NODE_EXTRA_CA_CERTS=/path/to/ca.pem. Same pattern with Python (certifi), Go, and curl built against different TLS backends. When the browser trusts a cert and your script doesn't, the trust store is the first place to look.


Where Crosscheck fits

DNS and TLS issues are some of the hardest bugs to file well because they happen at connection time, before the page can run any embedded bug-report widget. When something does load far enough to break, Crosscheck captures the screenshot, the screen recording, the console logs, and the network requests — including failed TLS and CORS errors — and sends a complete report to Jira, Linear, ClickUp, GitHub, or Slack in one step.

Try Crosscheck free

Related Articles

Contact us
to find out how this model can streamline your business!
Crosscheck Logo
Crosscheck Logo
Crosscheck Logo

Speed up bug reporting by 50% and
make it twice as effortless.

Overall rating: 5/5