Summary

  • Acceptance of 0-RTT data proves a transport decision, not that a business operation was committed exactly once.
  • Safe deployment needs an application-level receipt joining early-data handling, retry paths, anti-replay scope and the authoritative commit outcome.

Imagine a client resuming a connection to provision a service. It sends a POST before the handshake completes. One edge accepts the early data, but a disrupted response causes the client or an intermediary to retry through another edge. The dashboard records a fast connection and a successful response. The unresolved question is the one that matters: did the application create one service instance or two?

That scenario is not an allegation about a provider. It follows directly from the boundary the standards draw. TLS 1.3 permits early data when client and server share resumption material, but RFC 8446 says 0-RTT has weaker security properties than ordinary application data. In particular, it offers no guarantee of non-replay between connections. Within one connection, records cannot simply be duplicated and accepted twice; across a distributed serving system, the evidence problem is different.

Strong deployment-wide replay protection requires a common notion of what has already been accepted. RFC 8446 describes approaches that retain recently seen ClientHello information or otherwise coordinate anti-replay state. It also recognises the operational difficulty: not every deployment maintains globally consistent state. A ticket accepted locally can therefore say that one endpoint admitted early data without proving that another endpoint could not admit the same logical operation.

QUIC does not move this responsibility out of sight. RFC 9001 warns that application data received with 0-RTT can cause an application to process it multiple times. It requires an application protocol to define acceptable use and replay mitigations. The completed handshake is meaningful—it authenticates and establishes connection state—but it does not retroactively turn an earlier application action into a once-only transaction.

HTTP adds a necessary signal. RFC 8470 defines Early-Data: 1 so a request that travelled as early data on an earlier hop can retain that fact through an intermediary. A server cannot make such a request safe merely by waiting for its own handshake to finish. If it is unwilling to risk processing a potentially replayed request, it can return 425 Too Early; a subsequent retry is sent after the handshake.

This control is useful only if the signal survives the path and the application understands what the method does. RFC 9110 calls a method idempotent when multiple identical requests have the same intended effect as one. Safe methods are idempotent; PUT and DELETE are also idempotent but are not safe. POST does not acquire idempotent semantics merely because a client attaches an idempotency key. Conversely, an application can define a particular POST as retry-safe, but only if the key's scope, retention, conflict rules and authoritative result are real operating controls.

The practical mistake is to measure the wrong layer. A 0-RTT acceptance counter measures latency-path use. A TLS or QUIC handshake log measures connection state. An HTTP status records one response path. None independently proves how many durable application commits occurred, whether a retry hit a different replay domain, or whether a side effect escaped the resource transaction.

An acceptance record must join those layers without pretending they are one event. For a material state-changing request, record a request identity or idempotency key; method and target; resumption and early-data decisions; the edge, origin and anti-replay domain; propagation of the Early-Data signal; any 425 response and retry; the application transaction identifier; and the authoritative commit count and outcome. Secrets do not belong in the record, but the scope governing their acceptance does.

The purpose is not to ban 0-RTT. Read-only and deliberately replay-tolerant operations can benefit from it. The purpose is to prevent a latency optimisation from inheriting an assurance claim it cannot carry.

A fast handshake becomes an operationally safe result only when the application can show what happened after the transport accepted the bytes

Sources