Security
TLS 1.3 0-RTT: Decide Which Routes Are Safe to Replay
Review TLS 1.3 early-data routes for replay effects, including hidden GET side effects, distributed replay state and safe fallback to a normal handshake.
In this article
TLS 1.3 early data can let a returning client send application data before the new handshake finishes. That latency benefit comes with weaker replay guarantees than ordinary application data. Before enabling 0-RTT, identify which operations remain safe if the same request reaches your application more than once.
Session resumption and early data are related but not identical. You can use resumption without allowing application requests in 0-RTT. RFC 8446, especially its early-data and anti-replay sections, explains why encryption alone does not make early data replay-safe.
Start with application effects, not HTTP labels
A route named GET /download may look harmless while consuming a one-time entitlement, incrementing a billable counter, or triggering an export job. The method label does not erase those side effects.
Review the actual handler and downstream calls. Ask what changes if it runs twice, runs out of order, or runs concurrently on different nodes. Include audit events, quotas, emails, and third-party calls—not just the primary database row.
For a public static image, repeated retrieval may be acceptable. For a password-reset action or purchase confirmation, repeated processing can be harmful. Make the decision route by route instead of enabling early data for an entire authenticated application by default.
Distinguish transport replay from ordinary retries
Applications already need to handle duplicate requests caused by network retries. Early data adds another place where duplication can arise, including across distributed server state. A transport-level mitigation does not prove that all application effects are protected.
Suppose an invented download entitlement permits one export. Two requests reaching separate workers can both read “unused” before either marks it consumed. A replay cache at one edge location does not establish a global business invariant inside the application.
Protect the invariant at the system that owns it, using an appropriate atomic operation or transaction. Even then, assess the whole workflow: marking an entitlement consumed while failing to produce the export is a different correctness problem from producing two exports.
Map where early data terminates
Draw the actual path from client through edge, proxy, and application. Identify which component accepts early data, how it signals that fact downstream, and whether the origin can reliably distinguish such requests.
Do not trust a client-supplied header as proof of transport state. Only accept an early-data indication through a documented trusted-proxy boundary that strips or overwrites untrusted input appropriately.
If the application cannot determine whether a request arrived as early data, enforce the policy at the terminating component or leave early data disabled for that traffic. Guessing from request speed or session cookies is not a security control.
Create a small route decision table
For each candidate route, record its authentication requirement, mutation effects, external calls, replay decision, and fallback behavior. Add an owner who can confirm the handler’s real semantics.
An illustrative policy might allow immutable public assets, require review for personalized read endpoints, and deny early data for account changes and purchases. This is an example policy, not a universal route classification. Even apparently read-only responses can have privacy or resource-consumption implications.
Keep denied routes functional after a normal handshake. The optimization should not become a hidden availability requirement. A client whose early data is rejected needs a supported path to complete its intended safe operation.
Test duplication in a sandbox
Use an authorized staging environment and synthetic accounts. Invoke the same business request twice and concurrently through the paths your deployment actually uses. Verify authoritative state and external-effect records afterward.
Test multiple application workers, edge locations where feasible, and a restarted node. A design that depends on in-memory replay state can behave differently after a process restart or when traffic reaches a different server.
You do not need to demonstrate an exploit against a public service to perform this review. Controlled duplicate-request tests establish whether application effects are safe without sending crafted attack traffic to systems you do not own.
Measure whether the benefit justifies the scope
Compare latency with and without early data for the routes you have approved. Include clients that cannot resume, clients whose early data is rejected, and realistic network delay. Do not quote a theoretical round-trip saving as a measured improvement for every request.
Track rejection and fallback rates alongside errors. An optimization that saves time for a small subset while creating difficult failure modes may not be worth broad deployment. Keep the approved surface narrow enough to review after application changes.
When a route gains a new side effect, revisit its early-data status. A formerly safe content endpoint can become unsafe when a product feature adds charging, token consumption, or notification behavior.
Conclusion
Enable 0-RTT only where repeated processing is demonstrably safe. Separate resumption from early data, verify the trust boundary, and test application effects across workers before accepting a latency optimization with replay consequences.