QUIC with TLS 1.3 lets a resuming client send application data in its very first flight, known as 0-RTT. What does that buy, and what must a server do about replay?
answer
- 0-RTT = data in the first flight using a resumption PSK
- not forward-secret + replayable
- safe & idempotent only; else 425 Too Early
- Early-Data: 1 header from the terminating proxy
- per-node replay cache ≠ cluster protection; 3x amplification limit
basics
~20 s0-RTT saves one round trip on resumed connections by sending data under keys derived from a previous session. That data has no replay protection and is not forward-secret, so servers must accept only safe, idempotent requests in it, or reject with HTTP status 425 Too Early and make the client retry after the handshake.
solid answer
~1 minOn resumption the client holds a pre-shared key from an earlier session, so it can encrypt application data with a key derived from that ticket and attach it to its first flight — zero round trips before the request is on the wire. On a 100 ms path this removes 100 ms from every resumed connection, which is a big deal on mobile. The cost is that early data has weaker security properties. It is **not forward-secret** (compromise of the resumption secret exposes it), and crucially the server cannot prove it is fresh, so an attacker who captures the first flight can **replay** it. If the request mutates state, the operation happens twice. Defences, layered: - Accept early data only for **safe, idempotent** requests — typically GET/HEAD with no side effects. Reject anything else with **425 Too Early**, and the client retries once the handshake completes. - Use single-use session tickets and a bounded anti-replay window per server; note this is per-node unless you share state across a cluster. - Intermediaries add `Early-Data: 1` so origins know a request arrived in early data. - Anti-amplification still applies: a server must not send more than three times what it received before validating the client's address.
code
http · 7 linesPOST /v1/payments HTTP/3
:authority: api.example.com
early-data: 1
content-type: application/json
HTTP/3 425 Too Early
retry-after: 0go deeper
Know that 0-RTT sends the request immediately on a resumed connection, that the data can be replayed, and that only harmless read requests belong there.
Explain the resumption ticket, the loss of forward secrecy, and the 425 Too Early retry contract.
Own the policy: endpoint allow-listing, Early-Data propagation across proxies, ticket lifetime and single-use handling, and the cluster-wide replay gap.
Weigh a round trip against replay exposure per product surface, decide where 0-RTT is disabled outright, and design the shared-state or routing approach that makes replay windows meaningful across a fleet.
## The handshake economics A fresh QUIC connection completes the TLS 1.3 handshake in one round trip, then carries the request — 1-RTT to first byte of request. Compare with TCP + TLS 1.3, which needs a TCP handshake and then a TLS handshake before that. Resumption improves it further. When a client has connected before, the server may have issued a **session ticket** (a NewSessionTicket message) containing or referencing a pre-shared key (PSK). On the next connection the client can derive an *early data* key from that PSK and attach application data to the very first packet it sends. The server, on recognizing the ticket, can decrypt and process it before the handshake finishes. That is **0-RTT**: the request is on the wire at time zero. What you save is one full round trip on every resumed connection. On a 30 ms intra-region path that is minor; on a 150 ms mobile path where connections are re-established constantly as the radio sleeps, it is a visible product improvement. ## Why early data is dangerous Two distinct weaknesses: **1. No forward secrecy.** 1-RTT keys come from a fresh ephemeral key exchange for this connection. Early-data keys come from the stored resumption secret. If that secret is later compromised — a stolen ticket key, a compromised server — recorded early data can be decrypted. Everything after the handshake completes is forward-secret as usual. **2. No replay protection.** This is the serious one. The server has no way to know whether the first flight is live traffic or a recording. Anything within the client's first flight can be captured by a network attacker and re-sent, possibly many times and to different servers in the same cluster. The server will decrypt it, see a valid request, and execute it. If the request was `POST /transfer?amount=100`, that transfer happens again. A subtler variant: an attacker replays a request repeatedly to force cache pollution, to amplify load, or to observe timing differences. TLS 1.3 explicitly declines to solve this at the protocol level and pushes the responsibility to the application. ## What servers must do **Restrict by method and semantics.** The core rule from RFC 8470 ("Using Early Data in HTTP"): only requests that are **safe** and whose repetition is harmless should be processed from early data — in practice GET and HEAD of resources without side effects. Note that "safe method" is not automatically enough: a GET that increments a counter, sends an email, or triggers an expensive uncached computation is a bad early-data candidate. **Reject the rest with 425 Too Early.** The server answers 425, which tells the client: I will not process this yet, retry it after the handshake completes. Well-behaved clients retry automatically on the same connection once 1-RTT keys are available. This gives you a clean policy: allow-list what is replay-safe, 425 everything else. **Signal across hops.** When a TLS-terminating proxy forwards a request that arrived in early data, it adds the `Early-Data: 1` header field so the origin can apply its own policy and can itself answer 425. An origin must never treat the absence of that field as proof the request was not early data unless it controls the proxy. **Bound replay windows.** Servers should use single-use tickets where possible and keep an anti-replay cache of ticket identifiers with a short ticket lifetime. The hard part is clusters: an anti-replay cache on node A does not stop a replay landing on node B. Shared state (a fast replicated cache) or routing tickets back to their issuing node helps; otherwise treat 0-RTT as replayable across your whole fleet and rely on the application-level policy. **Disable it where the risk is not worth it.** For an API where most requests are mutating, 0-RTT buys little and costs review effort. Turning it off is a legitimate engineering decision. ## QUIC-specific extras - **Anti-amplification.** Before validating the client's address, a QUIC server must not send more than three times the bytes it received. This blunts using 0-RTT as a reflection/amplification vector, and it means large early responses may be paced until address validation completes. - **Address validation with Retry.** A server under load can respond with a Retry packet containing a token, forcing the client to prove it can receive at its claimed address before state is committed. - **Transport parameters are remembered.** A 0-RTT connection reuses remembered transport parameters (flow-control limits, stream counts) from the prior connection; the server must not reduce them below what the client assumed until the handshake confirms new values. - **Rejection is normal.** A server may refuse early data entirely; the client then re-sends that data after the handshake. Clients must therefore be able to buffer and resend, which is why 0-RTT requests must be replayable *by the client* as well. ## The interview-ready summary 0-RTT trades a round trip for two security properties: forward secrecy of the early data and freshness. The mitigation is not cryptographic, it is a policy: allow only requests whose repetition is harmless, answer everything else with 425 Too Early, propagate `Early-Data: 1` across hops, and remember that per-node replay caches do not protect a cluster.
- A GET request is a safe method — is it automatically fine to accept in early data?No. Safe means it should not change state, but plenty of real GET endpoints do: they increment counters, trigger background jobs, send notifications, or perform expensive uncached work that a replay can amplify into a denial of service. Decide per endpoint whether repeated execution is genuinely harmless, and answer 425 Too Early for the rest.
- Your anti-replay cache is per server instance. Is that enough?No. An attacker can replay the captured first flight toward a different instance behind the same load balancer, which has no record of the ticket being used. You need shared replay state, or routing that returns a ticket to its issuing node, or you must accept that 0-RTT is cluster-wide replayable and rely entirely on restricting early data to requests whose repetition is harmless.
It is like handing a pre-signed order slip at the door before anyone checks who you are: fine for "show me the menu", disastrous for "charge my account", and someone who photocopied your slip can hand it in again.
saying these in an interview costs you the question
- Treating 0-RTT as just a faster handshake with no security consequences
- Assuming TLS 1.3 prevents replay of early data — it explicitly leaves that to the application
- Accepting any GET in early data without checking whether repetition is harmless
- Believing a single-node anti-replay cache protects a load-balanced cluster
- Confusing 0-RTT with the 1-RTT full handshake, which is forward-secret and replay-protected