Summary

  • TCP keepalive asks whether a quiet peer can answer at the transport layer; it cannot prove that the peer application is healthy.
  • The mechanism stayed optional, configurable and off by default because an idle connection's silence has more than one possible cause.
  • One missed reply cannot establish death, while middlebox memory, user timeout and energy cost make any universal probe interval unsafe.

Reliability stopped where nothing had been sent

RFC 793 gave TCP a clear responsibility when data exist. Bytes enter a sequence space. Acknowledgments advance. Missing data trigger retransmission. Eventually an endpoint succeeds, receives a reset or reaches a configured failure policy.

An idle connection presents a different problem. There is no new byte awaiting an ACK and no unacknowledged byte whose timer can expose a broken path. The two endpoints may be alive and intentionally quiet. One host may have crashed. A cable, route, firewall or address mapping may have disappeared. From the local TCP state alone, all those worlds can look the same.

This is not a flaw in reliable delivery. It is a limit on what delivery evidence can say when no delivery is attempted.

The probe from just behind the frontier

RFC 1122 recorded TCP keepalive in 1989 as a disputed, optional mechanism. The customary probe carries SEG.SEQ = SND.NXT-1: a number immediately before the next new byte the sender could transmit.

That placement is deliberately awkward. It should fall outside the receiver's current acceptable frontier and cause the remote TCP to answer with an ACK showing what it still expects. The probe normally carries no data and does not advance the application stream. A one-byte “garbage” form remained configurable only to interoperate with erroneous implementations.

The reply answers a narrow question. A TCP endpoint, reached through some usable return path, processed this segment now. It does not say that the remote application consumed previous work, holds valid credentials, can reach its database or will answer the next business request.

Optionality was part of the safety model

RFC 1122 did not require every TCP to implement keepalive. If implemented, the application had to be able to turn it on or off for each connection, and the default had to be off. The idle interval had to be configurable and default to at least two hours.

Those constraints prevented transport machinery from silently choosing an application's failure semantics. A terminal session, routing adjacency, database pool and sleeping sensor assign different costs to false closure, slow detection, network traffic and retained state. The common transport layer cannot infer those costs from the absence of bytes.

Two hours was not a claim that a peer becomes dead at 7,200 seconds. It was a conservative default boundary: unsolicited probing should be rare unless the application deliberately selects another policy.

The consolidated modern specification, RFC 9293, preserves this arrangement. Decades of deployment did not turn the optional probe into an automatic definition of life.

Why one unanswered question proves nothing

TCP does not reliably retransmit pure ACKs as independent data. A keepalive probe may be lost. Its ACK may be lost. Congestion may delay either. Therefore RFC 1122 and RFC 9293 explicitly forbid treating failure to answer any one probe as proof of a dead connection.

This restraint matters because closure is an action, not an observation. Once local TCP tears down state and reports failure, an application may abandon a transaction, elect a replacement, release a lock or open another connection. The consequences can outlive the packet loss that caused the inference.

Repeated probes and a configured threshold can make failure more plausible. They still express a local risk decision: after this much missing evidence, retaining uncertainty costs more than closing. They do not transform absence into direct testimony.

User timeout answers a different question

TCP already had a user-timeout concept for data that remain unacknowledged. RFC 5482 later defined an option for communicating a timeout preference. Its question is not “has an idle peer spoken?” but “how long may transmitted data remain unacknowledged before this endpoint gives up?”

The distinction prevents conflicting clocks. RFC 5482 notes that some keepalive policies can abort a connection that would otherwise survive a temporary outage. When keepalive and the User Timeout Option are combined under that specification, the keepalive timer must exceed the adopted user timeout.

Application data, an idle probe and a close decision belong to related but different evidence chains. Conflating them makes a convenience timer overrule a more explicit delivery policy.

The intermediary developed its own memory

Early end-to-end discussions assumed the endpoints owned connection state. NATs and stateful middleboxes inserted another expiring ledger into the path. An idle TCP connection could remain valid at both hosts while an intermediary removed the mapping needed for the next packet.

RFC 5382 turned the two-hour default into a compatibility boundary. A qualifying NAT that cannot determine whether established endpoints remain active must not abandon the mapping before two hours and four minutes. The extra interval accommodates packets in flight.

That rule protects endpoint expectations without making the NAT an owner of the connection. It also does not prove universal compliance. Operators often encounter shorter state lifetimes and respond with more frequent probes. The response may preserve the mapping, but it is paying an intermediary tax: endpoints create traffic not for their own application semantics, but to keep a private box from forgetting them.

The battery exposed the opposite cost

More frequent probes sound harmless on a server connected to mains power. On an energy-constrained device, each transmission can wake a radio and extend an expensive active period. RFC 9006 captures the conflict: the long TCP default may fail to preserve some middlebox state, while shorter keepalives can drain a device's battery.

No transport constant solves that allocation. The application knows whether a delayed reconnect is acceptable. The operator knows the path's state lifetime. The device knows its energy budget. The correct policy depends on all three, and sometimes the rational choice is not to keep the connection alive at all.

A heartbeat that cannot hear the application

Transport keepalive is often described as a heartbeat, but it listens at the wrong layer for many failures. A kernel may ACK while the service process is deadlocked. A proxy may answer TCP while an upstream dependency has failed. A remote application may be healthy but deliberately suspended.

An application-layer heartbeat can ask a stronger, domain-specific question: can the service parse a request, consult its state and produce a valid response? That strength has its own cost and still cannot prove every future operation. The two mechanisms should not be stacked without knowing which failure each is intended to detect.

Keepalive also differs from zero-window probing. A zero-window probe ensures that reopening a receiver window is not lost when the peer has explicitly advertised no capacity. Keepalive begins when the connection is otherwise idle. Similar small packets protect different contracts.

Silence retained its presumption of innocence

The lasting design choice was not the peculiar SND.NXT-1 segment. It was the refusal to let a transport implementation turn silence into guilt by default.

A received ACK is evidence of recent TCP reachability, not application health. A missing ACK is uncertainty, not death. A configured series of misses can justify a local close because an application has chosen a failure budget, not because the Internet has supplied certainty.

Keepalive stayed optional because the endpoint that bears the consequence must retain the decision. TCP supplied a way to ask. It did not claim the authority to decide why nobody answered.

Sources and evidentiary limits

RFC 793 establishes original connection and user-timeout machinery. RFC 1122 records the 1989 keepalive contract. RFC 5382 defines NAT timing expectations. RFC 5482 separates user timeout from keepalive abort policy. RFC 9006 documents the energy trade-off. RFC 9293 provides the consolidated current rules.

These sources do not establish every platform default, middlebox timeout or application's health semantics.