Time to Live and Hop Limits

The Internet has no central map and no supervisor. Each router makes a purely local decision — "for this destination, my next hop is over there" — and hands the packet on. That decentralisation is the Internet's genius, but it hides a lurking danger: what if the routing tables briefly disagree and point at each other? A packet could be flung back and forth between two routers, or loop endlessly around a ring, consuming bandwidth forever and never arriving. Multiply that by millions of packets and a routing glitch would melt the network.

The safety valve is a single small counter carried in every IP packet: the Time to Live (TTL) in IPv4, renamed the Hop Limit in IPv6. Every router that forwards the packet decrements it by one; when it hits zero, the packet is thrown away and a polite complaint is mailed back to the sender. It is a self-destruct timer that guarantees no packet can wander the Internet forever — and, in a lovely twist, the very same mechanism is what lets traceroute draw you a map of the path your packets take.

The field: one byte, counting down

TTL lives in the IPv4 header as an 8-bit unsigned integer — so it ranges from 0 to 255. The rule the network layer enforces is deliberately dumb and therefore bullet-proof:

In IPv6 the field is functionally identical but was given the honest name Hop Limit, because — despite the words "time to live" — it has nothing to do with clocks. The original 1981 spec (RFC 791) imagined routers subtracting the number of seconds a packet had waited, so that TTL really was a lifespan in time. In practice routers forward in microseconds and nobody bothered with sub-second bookkeeping, so TTL quietly became a pure hop counter. IPv6 simply stopped pretending.

Watch the counter fall

Follow a packet launched with TTL = 4 across a path of routers. Each hop shaves one off the counter. If the destination is more than four hops away, the packet dies in transit and the router that killed it sends an ICMP Time Exceeded back — the packet never reaches the destination at all.

Notice the off-by-one that trips everyone up: a packet sent with TTL = n reaches a router n hops away carrying TTL = 1, and it is that router which decrements to 0 and drops it. So TTL = n lets a packet touch n routers but travel through the last one only as far as its own doorstep.

How traceroute turns a defence into a map

Here is the trick that makes TTL famous. Suppose you want to discover every router on the path to a distant host. You cannot ask the network for its map — none exists. But you can make each router in turn announce itself, by deliberately sending packets that are engineered to die at exactly the hop you want to probe:

The round-trip time of each reply also tells you the latency to that hop, which is why traceroute prints a growing ladder of routers with millisecond timings. The defensive self-destruct timer has been repurposed into the Internet's favourite diagnostic tool.

// A toy traceroute. We know the true path; the tool does NOT — it discovers // it hop by hop, using the ICMP Time Exceeded that each dying probe triggers. const truePath = ["10.0.0.1", "203.0.113.9", "198.51.100.4", "192.0.2.77"]; // hidden from the tool const DEST = "192.0.2.200"; // Simulate sending one probe: the router at index (ttl-1) is the one that // decrements to 0 and answers. If ttl reaches the destination, DEST answers. function sendProbe(ttl: number): { responder: string; reachedDest: boolean } { if (ttl > truePath.length) return { responder: DEST, reachedDest: true }; return { responder: truePath[ttl - 1], reachedDest: false }; } console.log(`traceroute to ${DEST}`); for (let ttl = 1; ttl <= 30; ttl++) { const { responder, reachedDest } = sendProbe(ttl); const rtt = (2 + ttl * 3 + Math.round(Math.random() * 4)); // pretend ms console.log(`${String(ttl).padStart(2)} ${responder.padEnd(15)} ${rtt} ms`); if (reachedDest) { console.log(" reached destination — done."); break; } }

Different operating systems start their packets at different TTLs: Linux and macOS use 64, Windows uses 128, and many routers and older Cisco/Solaris stacks use 255. Those are the three common initial values, and they are far enough apart to be a useful fingerprint.

When a reply arrives at you carrying, say, TTL = 57, you can reason backwards: the nearest "round" starting value at or above 57 is 64, so the packet almost certainly started at 64 (a Linux/macOS box) and crossed 64 - 57 = 7 routers to reach you. A reply with TTL = 250 started at 255 and is only 5 hops away. Network tools and passive-fingerprinting scanners lean on exactly this arithmetic to estimate both the remote OS and the hop distance — from a single packet, with no traceroute at all.

Other lives of the little counter

Because "drop me after N hops" is such a clean primitive, TTL gets borrowed for jobs beyond loop prevention:

The abbreviation is wildly overloaded, and conflating the meanings causes real bugs. The IP TTL on this page is a hop count in a packet header — a small integer, usually 64/128/255, decremented per router. But you will also meet a DNS record TTL, which is a duration in seconds telling resolvers how long to cache an answer (e.g. 300 s), and a cache/CDN TTL, a freshness lifetime for stored objects. Those two really are times; the IP one is not.

The trap: someone reads "IP TTL = 64" and imagines 64 seconds, or sets a DNS TTL of 64 thinking it controls routing. They are unrelated fields that happen to share a three-letter name. When you see TTL, first ask which layer — a packet header (hops), a DNS answer (cache seconds), or an HTTP/CDN object (freshness seconds).