§10.4
Chapter 10 · Connection Migration, Resilience, and Session Continuity

§10.4Session Resumption and Its Interaction with 0-RTT

RFC 9001RFC 9000

Learning objective. See where session resumption fits in QUIC's continuity story: when a connection is gone rather than merely moved, how a TLS session ticket restarts it cheaply, how 0-RTT and an address-validation token collapse the restart to a single round trip, and how this complements migration rather than duplicating it.

Three tiers of continuity #

QUIC offers a graded set of tools for keeping a session alive, and it helps to see them as a ladder from cheapest to most disruptive:

  1. Keep the connection warm. A keepalive PING under the idle-timeout and NAT horizons (§7.3) prevents the connection from being lost or rebound at all: no recovery needed.
  2. Migrate the connection. If the path changes (new network, NAT rebinding), connection migration (§10.1§10.3) keeps the same connection, with its keys and open streams, alive across the address change. No new handshake.
  3. Resume the session. If the connection is gone — the app was killed, the device was offline for minutes, the idle timeout fired — there is nothing to migrate. A new connection must be made, but session resumption makes that new connection cheap, and 0-RTT lets the first request ride in its opening flight.

Migration and resumption are often confused because both provide "continuity," but they operate on different objects: migration preserves one connection across a path change; resumption starts a new connection that inherits cryptographic and configuration state from an old one. This section is the third tier.

ServerClientConnection 1 — full handshakecaches PSK + token+ rememberedtransport paramsconnection fully lost — app killed /long offline (beyond migration's reach)Connection 2 — resume, laterPSK → derive earlykeys · tokenpre-validatesaddress (no Retryround trip)0-RTT only for replay-safe requests ·server MAY reject → client resends at1-RTThandshake → 1-RTT established,data flowsNewSessionTicket — PSK forresumptionNEW_TOKEN —address-validation token for nexttimeInitial + 0-RTT [request] + PSK +NEW_TOKEN, all in the firstflightresponse — answered at ~1 RTT(0-RTT saved the handshake)
Fig. 10.4-1Resumption with 0-RTT. Connection 1 issues a NewSessionTicket (a PSK) and a NEW_TOKEN (an address-validation token). When the connection is later lost entirely, connection 2 offers the PSK, the token, and a 0-RTT request all in the first flight: the PSK derives early keys, the token pre-validates the address (skipping a Retry), and the request is answered in about one round trip.RFC 9001 §4.6; RFC 9000 §7.4.1, §8.1

How resumption works, briefly #

Resumption reuses TLS 1.3's session-ticket mechanism (§2.3). During connection 1 the server issues a NewSessionTicket, a pre-shared key (PSK) the client caches ([RFC 9001 §4.6.1]). On a later connection the client offers that PSK in its ClientHello; the server recognizes it, and both derive keys without a full certificate exchange. The client also remembers the server's transport parameters from the earlier connection, because it will need them before the new server flight arrives ([RFC 9000 §7.4.1], and §7.1). Tickets are time-bounded: TLS caps the gap between the original connection and a resumption at seven days ([RFC 9001 §4.6]). They are also single-use on the server to limit replay.

The 0-RTT interaction #

Resumption is what enables 0-RTT: with a remembered PSK and remembered transport parameters, the client can encrypt and send application data (the request) in the same first flight as its Initial, before the handshake completes (§8.3). That data rides in 0-RTT packets as STREAM frames, coalesced with the Initial's CRYPTO frame (§2.3), and saves the handshake round trip so the response comes back in about one RTT.

Two constraints, both covered earlier, govern its safe use:

  • Replay-safe requests only. 0-RTT data is replayable, so only idempotent operations may use it: a client "SHOULD NOT" send non-idempotent requests as early data ([RFC 9114 §10.9], and §2.3, §8.3). A resumed GET is fine; a resumed payment is not.
  • Remembered limits must not shrink. Because the client is acting on the old transport parameters, a server that accepts 0-RTT "MUST NOT reduce" the receive limits below the remembered values ([RFC 9000 §7.4.1], and §7.1). And the server may always simply reject 0-RTT, in which case the client resends the same data at 1-RTT once the handshake completes, losing the round trip but nothing else.

Skipping the Retry: address-validation tokens #

Resumption pairs naturally with a second saving. Normally a server that wants to confirm a client's address before doing expensive work sends a Retry, costing a round trip (§8.3). To avoid that on a future connection, a server can issue a NEW_TOKEN frame during connection 1 ([RFC 9000 §8.1]); the client stores it and presents it in the Initial of connection 2. The token proves the client was reachable at its address recently, so the server can skip the Retry and proceed. And, importantly, it can begin sending beyond the anti-amplification limit sooner. Resumption thus removes two round trips at once: the handshake (via 0-RTT) and the address-validation Retry (via the token).

Worked example: reopening a killed app #

Trace Figure 10.4-1. A mobile app opens a QUIC connection, does its work, and receives a NewSessionTicket (PSK) and a NEW_TOKEN, which it persists. The user backgrounds the app; minutes later the OS kills it and the connection is long gone. Migration cannot help, because there is no connection to move. When the user reopens the app, it makes a new connection but offers the cached PSK, the NEW_TOKEN, and its first request (a safe GET to refresh the view) as 0-RTT, all in the opening flight. The server recognizes the PSK and derives early keys, accepts the token so it need not send a Retry, and answers the request in about one round trip. To the user the app "resumes instantly," even though the transport connection is brand new. Had the request been non-idempotent, the client would have withheld it from 0-RTT and sent it after the handshake instead.

Note

The security posture differs between the tiers, and it is worth being precise. Migration preserves a connection whose keys are fully forward-secret and never replayable — it is cryptographically uneventful. Resumption's 0-RTT data, by contrast, is protected by keys derived from a stored secret with no fresh server randomness, which is exactly why it is replayable and restricted to idempotent requests (§2.3). So prefer keeping a connection alive (keepalive, migration) when you can; fall back to resumption when you must, and confine 0-RTT to requests that can tolerate replay.

In practice

editorial Persist resumption state so it survives an app restart, not just in memory. The biggest real-world win from this section is a mobile or CLI client that writes the session ticket and NEW_TOKEN to durable storage, so that reopening after a kill resumes with 0-RTT instead of paying a full cold handshake — the difference a user feels as "instant" versus "spinner." Gate 0-RTT to safe methods in your client library (many do by default; confirm yours), and on the server, size the 0-RTT-accepted limits so you never have to reduce them across deployments, since a reduction only takes effect for clients that don't resume (§7.5). Resumption plus a persisted token is the cheapest cold-start you can offer.

Takeaways #

Session resumption is the continuity tier for a connection that is gone rather than merely moved: a TLS session ticket (PSK) lets a new connection skip the full handshake, 0-RTT carries the first replay-safe request in the opening flight, and a stored NEW_TOKEN skips the address-validation Retry — together collapsing a cold start to roughly one round trip. It complements migration (which preserves one connection across a path change) rather than replacing it. With the migration and continuity mechanisms all in place, §10.5 verifies a real migration on the wire with a packet capture.