Too Early
ActiveHTTP 425 Too Early indicates the server is unwilling to risk processing a request that might be replayed. Defined in RFC 8470 §5.2. Specifically designed for TLS 1.3 0-RTT early data replay protection — the client should retry after the handshake completes in 1-RTT mode.
Description
The 425 Too Early status code indicates that the server is unwilling to risk processing a request that might be replayed. TLS 1.3 introduced a feature called 0-RTT (zero round-trip time) that allows clients to send application data in the very first flight of a connection, before the TLS handshake fully completes. While this dramatically reduces latency, the early data lacks replay protection — an attacker who captures the initial ClientHello and early data can resend it to the server, potentially causing duplicate side effects.
The server returns 425 to signal that the request arrived as TLS 1.3 early data and the server is unwilling to process it in that context. The correct client behavior is simple: retry the exact same request over the same connection after the handshake completes (in 1-RTT mode). The request itself is perfectly valid — it's the transport-layer timing that makes it dangerous.
The replay risk is not theoretical. In 0-RTT, the server has no cryptographic guarantee that this is the first time it has seen this data. An on-path attacker can capture and replay the entire early data block. For idempotent requests like GET, replay is generally harmless — fetching a page twice changes nothing. But for non-idempotent operations (POST, PUT, DELETE), replay can mean double-charging a credit card, double-submitting an order, or double-deleting a resource.
Servers and intermediaries (CDNs, reverse proxies, load balancers) use 425 as a security boundary. Cloudflare, Nginx, and other infrastructure can be configured to reject 0-RTT for specific paths or methods — typically allowing GET requests through while blocking POST/PUT/DELETE. The Early-Data header (value '1') is sent by intermediaries to inform the origin server that the request was received as early data, giving the origin the final say on whether to process or reject it.
This is one of the most niche HTTP status codes — it exists at the intersection of TLS and HTTP, only relevant when 0-RTT is enabled and the server or intermediary determines that the replay risk outweighs the latency benefit. Most developers will never encounter it directly, but understanding it is critical for anyone configuring TLS 1.3 at scale.
Examples
POST /api/payments/charge HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer eyJhbGciOi...
Early-Data: 1
{"amount": 4999, "currency": "usd", "customer": "cus_abc123"}HTTP/1.1 425 Too Early
Content-Type: application/json
{"error": "too_early", "message": "Request received as TLS 1.3 early data. Retry after handshake completes.", "retry": true}DELETE /api/accounts/acc_789 HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOi...
Early-Data: 1HTTP/1.1 425 Too Early
Content-Type: application/json
Connection: keep-alive
{"error": "too_early", "message": "Non-idempotent requests are not accepted as early data. Please retry.", "hint": "This request will succeed after the TLS handshake completes (1-RTT)."}GET /api/user/profile HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOi...
Early-Data: 1HTTP/1.1 200 OK
Content-Type: application/json
{"id": "usr_456", "name": "Alice", "email": "alice@example.com"}Edge Cases
- •The Early-Data header (value '1') is added by intermediaries (proxies, CDNs) to inform the origin that the request was received as 0-RTT early data — the origin makes the final accept/reject decision.
- •GET requests are generally safe to process as early data (idempotent), but a GET that triggers side effects (logging a view, incrementing a counter) may still warrant 425 rejection.
- •Clients MUST be prepared to retry on 425 without user interaction — the retry should happen automatically over the same connection once the handshake completes.
- •A server that never accepts 0-RTT will never send 425 — the code only appears when 0-RTT is enabled but specific requests are deemed too risky.
- •CDNs like Cloudflare allow per-route configuration: allow 0-RTT for static assets and cacheable GETs, reject for API mutations. This is the most common real-world encounter with 425.
- •If a client receives 425 and the TLS handshake has already completed (1-RTT), something is misconfigured — the server or intermediary is incorrectly flagging the request as early data.
- •Connection coalescing in HTTP/2 and HTTP/3 complicates 0-RTT — a replayed early data request might arrive on a coalesced connection serving multiple origins, requiring careful routing.
When You'll See This
- →Payment API rejecting a charge request received as TLS 1.3 early data to prevent double-charging
- →CDN blocking POST/PUT/DELETE methods in 0-RTT while allowing GET through for cacheable content
- →Origin server receiving the Early-Data: 1 header from a reverse proxy and deciding to reject a state-changing operation
- →E-commerce checkout endpoint configured to never accept early data due to financial transaction risk
- →API gateway returning 425 for any request to /admin/* paths regardless of method to prevent replay attacks on privileged endpoints
- →Load balancer rejecting 0-RTT requests that carry session-mutating cookies
Implementation References
| Language | Constant |
|---|---|
| Go | http.StatusTooEarly |
| Rust | http::StatusCode::TOO_EARLY |
| Python | http.HTTPStatus.TOO_EARLY |
| Node.js | http.STATUS_CODES[425] |
| .NET | HttpStatusCode.TooEarly |
| Java | 425 (no built-in constant; use raw int) |
| PHP | Response::HTTP_TOO_EARLY (Symfony 5.4+) |
| Ruby | :too_early (Rack 3.0+) |
History
Defined in RFC 8470 (Using Early Data in HTTP), published September 2018. The code was specifically created to address the replay vulnerability introduced by TLS 1.3's 0-RTT feature (RFC 8446, August 2018). The Early-Data request header was defined in the same RFC to enable intermediaries to signal early data context to origin servers.
Related Status Codes
Related Headers
FAQ
What is TLS 1.3 0-RTT and why does it need HTTP 425?
TLS 1.3 allows clients to send application data in the first flight of a resumed connection (zero round-trip time) using a pre-shared key from a previous session. This eliminates one round trip of latency but sacrifices replay protection — an attacker can capture and resend the early data. HTTP 425 gives servers a standard way to reject requests that arrived as early data when the replay risk is unacceptable, telling the client to retry after the full handshake.
When should a server return 425 vs just processing the request?
Return 425 for non-idempotent requests (POST, PUT, DELETE) that could cause harmful side effects if replayed — payments, order submissions, account deletions, state mutations. Allow early data for idempotent, read-only requests (GET, HEAD) that are safe to replay. The decision point is: 'if an attacker replays this request, would it cause damage?' If yes, return 425.
How does the Early-Data header relate to HTTP 425?
The Early-Data request header (value '1') is added by TLS-terminating intermediaries (CDNs, load balancers, reverse proxies) to inform the origin server that the request was received as 0-RTT early data. The origin then decides whether to process it or return 425. Without this header, the origin has no way to know the request's TLS context when TLS is terminated at the edge. The header and the status code are defined in the same RFC (8470) as a paired mechanism.