# 425 Too Early: TLS 1.3 0-RTT Replay Protection

> 425 Too Early tells a client not to send a request in TLS early data because it could be replayed. See the Early-Data header and nginx config for 0-RTT.

Source: https://howhttpworks.com/status-codes/425
Last reviewed: 2026-10-04

> **TL;DR:** 425 Too Early is the server saying "this arrived in TLS 1.3 early data and I will not process it, because an attacker could replay it. Send it again after the handshake." It exists so 0-RTT can be used for resumption speed without replaying unsafe requests (RFC 8470).

## Why it exists

TLS 1.3 session resumption can carry application data in the very first flight (0-RTT). That saves a round trip, but early data is replayable: an attacker who records the packet can send it again to the server, which may process it twice. RFC 8446 §8 leaves protection to the application. RFC 8470 defines the HTTP half:

1. A TLS-terminating proxy (CDN, load balancer, nginx) that accepts early data adds `Early-Data: 1` to the request it forwards.
2. The origin, which knows whether the operation is safe to repeat, either handles the request, or answers `425 Too Early`.
3. The client retries it once the handshake has completed, which can no longer be replayed.

```http
POST /orders HTTP/1.1
Host: shop.example.com
Early-Data: 1
Content-Type: application/json

HTTP/1.1 425 Too Early
```

## Configure it

nginx must opt in to early data and tell the upstream:

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

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

`$ssl_early_data` is `1` when the request was received in early data and the handshake had not completed, and empty otherwise. The origin then rejects unsafe methods:

```javascript
app.use((req, res, next) => {
  const early = req.headers['early-data'] === '1'
  if (early && !['GET', 'HEAD', 'OPTIONS'].includes(req.method)) {
    return res.status(425).end()
  }
  next()
})
```

A GET that has side effects (logging in via a link, burning a one-time code) is not safe either, and RFC 8470 says the application decides, not the method name.

## Notes

- A TLS-terminating proxy that cannot tell the origin about early data (no `Early-Data` header support upstream) should not enable `ssl_early_data`, because the origin would have no way to refuse unsafe requests.
- CDNs that offer 0-RTT as a switch (Cloudflare calls it 0-RTT Connection Resumption) forward the signal to the origin; check the vendor docs for the exact header they set.
- 425 is not cacheable by default and is not an indication of a broken server. Elevated 425s after enabling `ssl_early_data` are expected for returning clients.
- Without `ssl_early_data on`, nginx never accepts early data, and you will never see this code from it.

## Try it with curl

`425 Too Early` is sent by an origin that sees `Early-Data: 1` on a request it will not process. That header is normally added by a TLS-terminating proxy, not by clients, so you can simulate it by sending it directly to an origin that implements RFC 8470. curl 8.7 does not send real TLS 1.3 early data.

```bash
curl -i -X POST https://origin.example.com/orders -H 'Early-Data: 1' -d '{"sku":"A1"}'
```

If the origin supports the check, the response is `HTTP/1.1 425 Too Early`. Without that support, the header is ignored.

## Related

- [429 Too Many Requests](https://howhttpworks.com/status-codes/429)
- [409 Conflict](https://howhttpworks.com/status-codes/409)
- [426 Upgrade Required](https://howhttpworks.com/status-codes/426)
- [HTTPS and TLS](https://howhttpworks.com/guides/https-and-tls)
