../

Communication networks

How bytes get from a server to a browser and where the time goes: latency, IP, DNS, TCP, UDP, TLS, radio networks, HTTP/1.1 to HTTP/3, and the browser transports built on them. Follows High Performance Browser Networking, updated for QUIC, TLS 1.3 and 2026 browsers. Fetch-level caching and headers live in Fetch API.

Latency and bandwidth

Latency is the time for one bit to reach the other side; RTT is there and back. Bandwidth is the maximum throughput of a path. Most web pages are latency-bound: past ~5 Mbit/s, more bandwidth barely changes page load time, while every RTT cut does.

ComponentCauseLever
Propagationdistance ÷ signal speedmove servers closer (CDN, edge)
Transmissionbytes ÷ link ratesend fewer bytes (compression, smaller images)
Processingrouters, firewalls, TLS, app serversfewer hops, faster backends
Queuingpackets waiting in buffers (bufferbloat)AQM (fq_codel, CAKE), BBR

Light in fiber travels at about 200,000 km/s (refractive index ~1.5), or 5 µs per km.

RouteDistanceVacuum one-wayFiber one-wayFiber RTT (best case)
New York → San Francisco4,130 km14 ms21 ms41 ms
New York → London5,570 km19 ms28 ms56 ms
San Francisco → Tokyo8,280 km28 ms41 ms83 ms
London → Sydney16,990 km57 ms85 ms170 ms
Half the equator20,040 km67 ms100 ms200 ms

Real RTTs run 1.5 to 3 times higher because of indirect routing, queuing and the last mile.

DelayFeels
0–100 msinstant
100–300 msslight lag
300–1000 ms"the machine is working"
over 1 sattention drifts
over 10 stask abandoned
  • Last mile: the access link (DSL, cable, fiber, Wi-Fi, cellular) often adds more latency than the backbone. Fiber-to-the-home is ~1–5 ms, cable 10–20 ms, mobile far more.
  • Bandwidth-delay product (BDP) = bandwidth × RTT = bytes in flight needed to fill the pipe. 100 Mbit/s × 80 ms = 1 MB, so TCP windows must reach 1 MB.

Network layers

OSILayerProtocolsUnitAddress
7ApplicationHTTP, DNS, WebSocket, SMTP, SSHmessageURL, hostname
6Presentationencoding, compression, TLS (arguably)
5SessionTLS sessions, RPC
4TransportTCP, UDP, QUIC (on UDP)segment / datagramport
3NetworkIPv4, IPv6, ICMPpacketIP address
2Data linkEthernet, Wi-Fi (802.11), ARP / NDPframeMAC
1Physicalcopper, fiber, radiobit

The TCP/IP model folds 5–7 into Application and 1–2 into Link. HTTP/3 blurs the lines: QUIC does transport, TLS and multiplexing in user space on top of UDP.

HTTP/1.1, HTTP/2           HTTP/3
+----------------+         +----------------+
| HTTP           |         | HTTP/3 (QPACK) |
+----------------+         +----------------+
| TLS 1.2 / 1.3  |         | QUIC + TLS 1.3 |
+----------------+         +----------------+
| TCP            |         | UDP            |
+----------------+---------+----------------+
|                IP (v4 / v6)               |
+-------------------------------------------+
SizeValue
Ethernet MTU1500 bytes
TCP MSS1460 (IPv4) / 1440 (IPv6) payload bytes
Minimum IPv6 MTU1280 bytes
QUIC minimum datagram1200 bytes
Initial TCP cwnd (RFC 6928)10 segments ≈ 14.6 KB

IP addressing

IPv4Meaning
192.0.2.10/24address with a 24-bit prefix: network 192.0.2.0, 256 addresses (254 hosts)
/32, /31, /30, /24, /16, /81, 2 (point-to-point), 4, 256, 65,536, 16.7 M addresses
10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16private (RFC 1918), need NAT to reach the internet
100.64.0.0/10carrier-grade NAT (shared ISP space)
127.0.0.0/8loopback
169.254.0.0/16link-local (no DHCP); cloud metadata at 169.254.169.254
0.0.0.0"any address" when binding a server
224.0.0.0/4multicast
IPv6Meaning
2001:db8::1:: replaces one run of zero groups; leading zeros dropped
::1loopback
fe80::/10link-local, always present on every interface
fc00::/7 (fd00::/8 in practice)unique local addresses, the private range
2000::/3global unicast
/64one subnet (SLAAC needs it); sites usually get a /48 or /56
::ffff:192.0.2.10IPv4-mapped address
[2001:db8::1]:443brackets in URLs and host:port
  • Addresses per prefix: 2^(32 - n) for IPv4, 2^(128 - n) for IPv6.
  • Happy Eyeballs (RFC 8305): clients race IPv6 and IPv4 connects and keep the winner, so a broken AAAA record costs ~250 ms rather than a timeout.
  • Subnet and firewall basics for servers are in Sysadmin.

DNS

browser/OS stub --> recursive resolver (ISP, 1.1.1.1, 8.8.8.8)
                     |  cache hit? answer now
                     +--> root (.)       "ask .com"
                     +--> TLD (.com)     "ask ns1.example.com"
                     +--> authoritative  "A 192.0.2.10, TTL 300"

A cold lookup costs several RTTs; a cached one costs nothing. Browsers, the OS and the resolver all cache until the TTL expires.

RecordHoldsNotes
A / AAAAIPv4 / IPv6 addressseveral records = simple load spreading
CNAMEalias to another namenot allowed at the zone apex
ALIAS / ANAME / flatteningapex alias (provider feature)resolved server-side to A/AAAA
HTTPS / SVCBendpoint hints: alpn="h3,h2", ipv4hint, ECH configlets clients use HTTP/3 and ECH on the first connection
MXmail servers with priority
TXTfree text: SPF, DKIM, DMARC, domain verification
NSauthoritative servers for the zoneset at the registrar too
SOAzone serial, refresh, negative-cache TTL
CAAwhich CAs may issue certificates0 issue "letsencrypt.org"
SRVhost and port for a service_sip._tcp style
PTRreverse lookup (IP → name)mail servers care
DS / DNSKEY / RRSIGDNSSEC chain of trust
TTLWhen
60–300 srecords you may fail over or migrate soon
3600 snormal
86400 sstable (NS, MX)
lower it a day before a changeold TTLs keep caches pinned until they expire
TransportPortNotes
Classic DNS53 UDP/TCPplaintext, spoofable without DNSSEC
DNS over TLS (DoT)853 TCPRFC 7858, Android "Private DNS"
DNS over HTTPS (DoH)443RFC 8484, browsers' secure DNS
DNS over QUIC (DoQ)853 UDPRFC 9250

In pages: <link rel="dns-prefetch" href="https://cdn.example.com"> for origins you might use, preconnect for origins you will use.

TCP

Reliable, ordered byte stream with flow and congestion control.

client                              server
  | SYN seq=x                          |
  |----------------------------------->|
  |                  SYN-ACK seq=y ack=x+1
  |<-----------------------------------|
  | ACK ack=y+1  (+ first data)        |   1 RTT before data
  |----------------------------------->|
MechanismWhat it doesImpact
Three-way handshakeagree on sequence numbers1 RTT per new connection
Flow control (rwnd)receiver advertises free bufferslow readers throttle senders
Window scalingwindows beyond 64 KB (RFC 7323)needed for BDP over 64 KB
Slow startcwnd starts at 10 segments, doubles each RTTnew connections can't use full bandwidth
Congestion avoidanceadditive increase after ssthresh, cut on lossthroughput sawtooth
Fast retransmit / recoveryresend after 3 duplicate ACKsavoids a full timeout
Slow-start restartcwnd reset after idlehurts long-lived idle connections; disable on servers
CUBICLinux default; loss-basedfills buffers, bufferbloat
BBRmodels bandwidth and RTT, not lossbetter on lossy/long links; pair with fq qdisc
Head-of-line blockingone lost segment stalls all later byteswhy HTTP/2 over lossy links suffers
Nagle's algorithmbatches small writesdisable (TCP_NODELAY) for interactive traffic
TIME_WAITcloser keeps the 4-tuple ~60 s (2 × MSL)ephemeral port exhaustion under many short outbound connections
TCP Fast Opendata in the SYN on repeat visitsrarely deployed; middleboxes break it

Round trips to fetch N bytes on a fresh connection, ignoring loss: ceil(log2(N / 14.6 KB + 1)). A 64 KB response needs 3 RTTs of slow start, so keep the critical HTML and CSS within the first ~14 KB.

Linux server tuning
sysctl net.ipv4.tcp_available_congestion_control
sysctl -w net.core.default_qdisc=fq
sysctl -w net.ipv4.tcp_congestion_control=bbr
sysctl -w net.ipv4.tcp_slow_start_after_idle=0
sysctl -w net.ipv4.tcp_tw_reuse=1        # outbound only
sysctl -w net.core.somaxconn=4096        # accept backlog
ss -ti state established '( dport = :443 )'  # cwnd, rtt

Persist settings in /etc/sysctl.d/*.conf. Best wins come from the app: reuse connections (keep-alive, pools), send fewer bytes, and cut round trips.

UDP and NAT traversal

UDP adds ports and a checksum to IP, nothing else: no handshake, ordering, retransmission, or congestion control. QUIC, DNS, WebRTC media and games build what they need on top.

NAT factConsequence
Many private hosts share one public IPinbound connections have nowhere to go
UDP mappings expire fast (often 30 s)send keepalives every 15–25 s
Symmetric NAT maps per destinationSTUN addresses don't work; need a relay
CGNAT stacks two NATsport forwarding impossible for the user
PieceRole
STUN (RFC 8489)"what is my public IP:port?" via a server
TURN (RFC 8656)relay traffic through a server when direct fails
ICE (RFC 8445)gather candidates (host, server-reflexive, relay), test pairs, pick the best

Most peer-to-peer sessions connect directly; a minority (corporate networks, symmetric NAT) need TURN, so production WebRTC always ships one. See WebRTC.

TLS

TLS 1.2 (2 RTT)                    TLS 1.3 (1 RTT)
C: ClientHello           -->       C: ClientHello + key_share  -->
S: ServerHello, Cert,    <--       S: ServerHello + key_share,
   ServerKeyExchange, Done            {Cert, Verify, Finished} <--
C: ClientKeyExchange,              C: {Finished} + HTTP request -->
   ChangeCipherSpec, Fin -->
S: ChangeCipherSpec, Fin <--
C: HTTP request          -->
 
TLS 1.3 resumption with 0-RTT
C: ClientHello + PSK + early data (HTTP request) -->
S: ServerHello ... + HTTP response               <--
FeatureWhat it isNotes
TLS 1.3 (RFC 8446, revised as RFC 9846 in 2026)1-RTT handshake, forward secrecy always, AEAD onlydisable 1.0/1.1; keep 1.2 for old clients
Key exchangeX25519; hybrid X25519MLKEM768 (post-quantum)default in current Chrome, Firefox, Safari, Cloudflare
Cipher suites (1.3)TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256ChaCha wins on phones without AES hardware
Session resumption1.2: session IDs/tickets; 1.3: PSK ticketsskips certificate work; still 1 RTT
0-RTT early datarequest sent with the ClientHello on resumptionreplayable: only for idempotent GET; servers answer 425 Too Early otherwise
SNIhostname in the ClientHellolets one IP serve many certs; visible to the network
ECH (RFC 9849)encrypts the inner ClientHello, including SNIkey published in the HTTPS DNS record; needs DoH to be meaningful
ALPNnegotiates h2 / http/1.1 inside the handshakeno extra RTT; h3 is discovered separately
Certificate chainleaf + intermediates (server sends), root (client has)missing intermediate = errors on some clients
RevocationOCSP stapling is fading; CRLs, CRLite, CRLSetsLet's Encrypt ended OCSP in 2025
Certificate lifetimeCA/B Forum max: 200 days (from Mar 2026), 100 (2027), 47 (2029)automate with ACME
HSTSStrict-Transport-Security: max-age=63072000; includeSubDomains; preloadno plain-HTTP first hop after the first visit (or ever, when preloaded)

TLS costs one extra RTT per new connection (two on 1.2) plus a few KB for certificates: prefer ECDSA certificates (smaller, faster) and keep the chain short.

Wireless and mobile

NetworkTypical RTT to the internetNotes
Wired / fiber1–10 ms accessstable
Wi-Fi 5/6/7+2–20 ms, spikyshared half-duplex medium; interference, retries
3G100–500 mslargely switched off
4G LTE30–100 mscontrol-plane promotion 50–100 ms when idle
5G (NSA)20–40 msLTE core, 5G radio
5G (SA)10–20 mslower with edge compute; mmWave rarely available
  • Radio power states (RRC): the modem sleeps (RRC_IDLE, 5G adds RRC_INACTIVE), and the first packet after idle pays a promotion delay before any data moves.
  • After traffic stops the radio stays high-power for seconds (tail time). Periodic polls and beacons keep it awake and drain the battery.
  • Batch requests, defer analytics (navigator.sendBeacon on page hide), prefer push over polling, and treat every mobile request as "may take a second or fail".
  • Mobile links change IPs (Wi-Fi ↔ cellular): TCP connections die, QUIC migrates.

HTTP/1.1

FeatureStatus
Persistent connections (keep-alive)default; reuse saves the TCP + TLS handshakes
Pipeliningspecified, never enabled by browsers (HOL, broken proxies)
6 connections per originbrowser limit; requests queue behind it ("Stalled" in DevTools)
Chunked transfer encodingstream a body of unknown length
Text headers, repeated per requestcookies and user agents resent every time
HTTP/1.1-era hackToday (HTTP/2+)
Domain sharding (static1., static2.)harmful: extra DNS, TCP and TLS, breaks prioritization
Concatenating all JS/CSS into one bundlesplit by route; fine-grained files cache better
Image spritesuse SVG or separate images
Inlining assets as data: URIsonly for tiny critical assets; kills caching
Cookie-less domainsstill reduces request size, but costs a connection

HTTP/2

One TCP connection per origin, carrying many interleaved streams of binary frames.

FeatureWhat it does
Binary framingHEADERS, DATA, SETTINGS, WINDOW_UPDATE, RST_STREAM, GOAWAY frames
Multiplexingmany concurrent requests on one connection; no app-level HOL
HPACK (RFC 7541)static + dynamic table header compression; repeated headers cost bytes, not KB
Per-stream flow controla slow stream can't starve the others
PrioritiesRFC 9218 Priority: u=0..7, i header plus fetchpriority in HTML
Connection coalescingreuse one connection for hosts sharing an IP and certificate
Server pushremoved from Chrome (106) and Firefox (132); use 103 Early Hints and preload

Remaining weakness: all streams share one TCP byte stream, so one lost packet stalls every stream (TCP head-of-line blocking). On lossy mobile links HTTP/2 can lose to several HTTP/1.1 connections.

HTTP/3 and QUIC

QUIC (RFC 9000) is a transport over UDP with TLS 1.3 built in; HTTP/3 (RFC 9114) maps HTTP onto it with QPACK (RFC 9204) header compression.

TCP + TLS 1.3 + HTTP/2           QUIC + HTTP/3
C: SYN              -->          C: Initial (ClientHello)      -->
S: SYN-ACK          <--          S: Initial + Handshake        <--
C: ACK, ClientHello -->          C: Handshake Fin + request    -->
S: ServerHello...   <--          S: response                   <--
C: Fin + request    -->
S: response         <--          1 RTT to first byte (0 on resume)
2 RTT to first byte
FeatureBenefit
Independent streamsa lost packet stalls only its own stream
Combined transport + crypto handshake1 RTT new, 0-RTT on resumption
Connection IDsconnection survives IP/port changes (Wi-Fi → 5G)
Encrypted headers and ACKsmiddleboxes can't ossify the protocol
User-space implementationcongestion control ships with the browser/server
TopicDetail
DiscoveryAlt-Svc: h3=":443"; ma=86400 on an HTTP/2 response, or alpn="h3" in the HTTPS DNS record
Fallbackbrowsers race TCP; UDP/443 blocked by some corporate networks
Costmore server CPU than kernel TCP; UDP GSO/GRO helps
ServersCloudflare, Fastly, Akamai, Caddy (default), nginx (listen 443 quic), HAProxy, Envoy
Load balancersmust route by connection ID, not 4-tuple, or migration breaks

Browser transports

APIDirectionTransportUse forWatch out
fetchrequest → response; streamed bodiesHTTP/1.1, 2, 3APIs, uploads, streaming responsesrequest streaming needs HTTP/2+ and duplex: "half"
Server-Sent Events (EventSource)server → client, textHTTPnotifications, LLM tokens, live feedsauto-reconnect with Last-Event-ID; 6-connection cap on HTTP/1.1
WebSocketfull duplex, messagesTCP (upgrade from HTTP/1.1; RFC 8441 on h2)chat, collaboration, gamesno built-in backpressure (WebSocketStream fixes it in Chromium); proxies time out idle sockets
WebRTC data channelpeer to peer, reliable or notSCTP over DTLS over UDPP2P files, low-latency game stateneeds signaling plus STUN/TURN
WebRTC mediapeer to peer audio/videoSRTP over UDPcalls, conferencingSFU for more than a few peers
WebTransportclient ↔ server streams + datagramsHTTP/3 (QUIC)low-latency media, games, unreliable updatesBaseline since 2026 (Safari 26.4); needs an HTTP/3 server

Server side: WebSockets, Streaming.

Performance checklist

GoalTechnique
Fewer round tripsHTTP/2 or 3, TLS 1.3, keep-alive, avoid redirects (each costs DNS + TCP + TLS + RTT)
Shorter round tripsCDN / edge for static and cacheable HTML; regional APIs near users
Warm connections early<link rel="preconnect" href="https://api.example.com" crossorigin> for 2–4 critical origins
Start work during server think time103 Early Hints with Link: </app.css>; rel=preload; as=style (HTTP/2+)
Fetch critical assets firstpreload, fetchpriority="high" on the LCP image, async/defer scripts
Avoid transfersCache-Control: max-age=31536000, immutable on hashed assets; ETag + no-cache on HTML
Smaller transfersBrotli (br) or zstd for text (Chrome 123+, Firefox 126+, Safari 26+ partial); AVIF/WebP images
Delta updatesCompression Dictionary Transport (RFC 9842): Use-As-Dictionary, dcb / dcz encodings
Fewer bytes on the wire per requesttrim cookies; HPACK/QPACK handle the rest
Fewer originsself-host fonts and critical third-party scripts
Mobilebatch, prefetch on Wi-Fi, back off retries with jitter, handle offline
MeasureRUM (Resource Timing, Web Vitals), not only lab tests

Diagnostic tools

ToolShowsExample
pingreachability, RTT, loss (ICMP)ping -c 10 example.com
traceroute / tracepathhops and per-hop latencytraceroute -T -p 443 example.com
mtrtraceroute + continuous loss per hopmtr -rwzbc 100 example.com
dig / drill / kdigDNS answers, TTLs, delegationdig +trace example.com
curl -wDNS, connect, TLS, TTFB, total timingssee recipe
curl --http3-onlywhether a host speaks HTTP/3see recipe
openssl s_clientcertificate chain, TLS version, ALPNopenssl s_client -connect host:443 -servername host
sssockets, states, cwnd, RTT per connectionss -tanp, ss -ti
tcpdumpraw packetstcpdump -i any -nn 'port 443' -w cap.pcap
Wiresharkdecoded packets; decrypt TLS with SSLKEYLOGFILESSLKEYLOGFILE=keys.log curl …
iperf3raw throughput between two hostsiperf3 -c host -R
Chrome DevTools → Networkper-request timing, protocol column (h2, h3), throttlingright-click headers → Protocol
chrome://net-exportfull network log for NetLog Viewercapture a bug report
WebPageTest / Lighthousewaterfalls, connection view, filmstripstest from real locations and devices

DevTools timing phases: Queueing → Stalled → DNS Lookup → Initial connection (TCP) → SSL → Request sent → Waiting for server response (TTFB) → Content Download.

Recipes

curl timing breakdown

When you want to know whether DNS, connect, TLS or the server is slow.

cat > curl-timing.txt <<'EOF'
     dns  %{time_namelookup}s\n
 connect  %{time_connect}s\n
     tls  %{time_appconnect}s\n
    ttfb  %{time_starttransfer}s\n
   total  %{time_total}s\n
protocol  HTTP/%{http_version} %{remote_ip}\n
EOF
curl -so /dev/null -w @curl-timing.txt https://example.com
# times are cumulative from the start: tls - connect = TLS

Trace a DNS lookup

When a record looks wrong, stale, or differs between resolvers.

dig +trace example.com                 # root -> TLD -> auth
dig +short example.com A @1.1.1.1      # one resolver
dig example.com AAAA +noall +answer    # with TTL left
dig example.com HTTPS +short           # alpn, ech hints
dig NS example.com +short              # who is authoritative
dig -x 192.0.2.10 +short               # reverse (PTR)

Check HTTP/3 support

When you want to confirm a site advertises and actually serves HTTP/3.

curl -V | grep -o HTTP3                 # curl built with h3?
curl -sI https://cloudflare.com | grep -i alt-svc
curl -sI --http3-only https://cloudflare.com | head -1
dig cloudflare.com HTTPS +short         # alpn="h3,h2"

In Chrome, enable the Protocol column in the Network panel; h3 means QUIC was used.

Inspect a certificate chain

When a client complains about an untrusted or expired certificate.

openssl s_client -connect example.com:443 \
  -servername example.com -alpn h2,http/1.1 \
  -showcerts </dev/null 2>/dev/null \
  | grep -E 's:|i:|Protocol|ALPN'
echo | openssl s_client -connect example.com:443 \
  -servername example.com 2>/dev/null \
  | openssl x509 -noout -dates -subject -ext subjectAltName

Measure connection phases in the browser

When you want real-user DNS, TCP, TLS and TTFB numbers instead of lab ones.

resource-timing.ts
type Phases = Record<
  "dns" | "tcp" | "tls" | "ttfb" | "download",
  number
>;
 
function phases(e: PerformanceResourceTiming): Phases {
  const tlsStart = e.secureConnectionStart;
  return {
    dns: e.domainLookupEnd - e.domainLookupStart,
    tcp: (tlsStart || e.connectEnd) - e.connectStart,
    tls: tlsStart ? e.connectEnd - tlsStart : 0,
    ttfb: e.responseStart - e.requestStart,
    download: e.responseEnd - e.responseStart,
  };
}
 
new PerformanceObserver((list) => {
  for (const e of list.getEntriesByType("resource")) {
    const r = e as PerformanceResourceTiming;
    console.table({ url: r.name, protocol: r.nextHopProtocol,
      ...phases(r) });
  }
}).observe({ type: "resource", buffered: true });

Cross-origin entries report zeros unless the server sends Timing-Allow-Origin. Reused connections show 0 for DNS, TCP and TLS.

References