HTTP 425 Too Early

Too Early

HTTP 425 is Too Early: the server is unwilling to risk processing a request that might be replayed (RFC 8470 section 5.2). It exists for TLS 1.3 early data, also called 0-RTT. When a client resumes an earlier session, it can send its first request before the handshake finishes and save a round trip, but anyone who recorded that early data can send it again, and the server can’t always tell the copy from the original.

How the pieces fit

  • The client that sends a request in early data must be ready for a 425. RFC 8470 says a user agent should retry automatically, and never in early data again; Firefox does.
  • A TLS-terminating proxy that forwards a request before its own handshake completes must add Early-Data: 1, so the origin knows the request is replayable.
  • The origin must answer 425 to a request marked Early-Data: 1 that it can’t safely process. It shouldn’t send 425 otherwise, since a client that didn’t use early data may not know what to do with it.
  • Proxies in between must pass a 425 back when the request they forwarded carried Early-Data.
POST /api/transfers HTTP/1.1
Host: bank.example
Early-Data: 1
Content-Type: application/json

HTTP/1.1 425 Too Early

The client sends the request again once the handshake is done, and the proxy forwards it without Early-Data.

Which requests to let through

Replays hurt only requests with side effects. A common rule is to accept GET, HEAD and OPTIONS in early data and answer 425 to everything else. That only holds if your GETs really are safe: a GET /unsubscribe?id=... that changes state shouldn’t run from early data either.

nginx

nginx’s documentation pairs ssl_early_data with forwarding the $ssl_early_data variable, which is 1 while the handshake is incomplete:

server {
    listen 443 ssl;
    ssl_protocols TLSv1.3;
    ssl_early_data on;

    location / {
        proxy_pass http://app;
        proxy_set_header Early-Data $ssl_early_data;
    }
}

The app then refuses what it won’t risk:

app.use((req, res, next) => {                    // Express
  if (req.get('Early-Data') === '1' && !['GET', 'HEAD', 'OPTIONS'].includes(req.method)) {
    return res.status(425).end();
  }
  next();
});

Gotchas

  • HTTP/3 too. QUIC has 0-RTT, and RFC 9114 requires RFC 8470’s replay protections for HTTP/3.
  • Not a rate limit or an outage. That’s 429 or 503; a 425 is about one request’s timing.
  • Caching. RFC 8470 says a 425 isn’t cacheable by default, and its body doesn’t represent any resource.

Search 425 429 in the tool below to compare it with Too Many Requests.

Try it in the HTTP Status Codes

425 Too Early

  1. 425Too Early4xx Client error

    The server will not risk processing a request that might be a replay; the client should send it again, later.

    When to use it

    TLS 0-RTT data, where an early request could be a copy of one already seen.

    Common mistakes

    Reaching for it as a rate limit, which is 429, and treating it as final rather than as a request to retry.

    Not cacheable by defaultRFC 8470

Every code, by class

Codes marked unofficial come from one server’s own convention — nginx, Cloudflare, cPanel, Microsoft — and are not in the IANA registry.

Runs in your browser. Nothing you enter is sent anywhere.