HTTP
425 - Too Early
Hitting a 425 Too Early means the server rejected your request because it might be a replay attack—the request arrived too early in a timing window, or replay protection detected a potential duplicate. This client-side error (4xx) is used in HTTP/2 Server Push and HTTP/3 to prevent replay attacks. Most common when requests are sent too quickly after a previous request, but also appears when timing windows are violated, nonces are reused, or replay protection mechanisms trigger.
#Common Causes
- →Frontend: Request sent too early after previous request. Missing or invalid timestamp in request. Nonce reused or missing. Request timing violates server window. Rapid retries trigger replay protection.
- →Backend: Replay attack prevention middleware detects early request. Timing window validation fails. Nonce validation rejects request. HTTP/2 push timing issue. Server replay protection active.
- →Infrastructure: Load balancer replay protection. API gateway timing validation. WAF replay attack detection. Network timing issues cause early arrival.
✓Solutions
- 1Step 1: Diagnose - Check DevTools Network tab Request timing—review request timestamps. Verify if requests are sent too quickly. Check Retry-After header. Review request timing patterns.
- 2Step 2: Diagnose - Server logs show timing window violations. Review replay protection configuration. Check nonce validation. Examine timing window settings.
- 3Step 3: Fix - Client-side: Wait for Retry-After duration before retrying. Add proper timestamps to requests. Generate unique nonces for each request. Implement proper request timing.
- 4Step 4: Fix - Server-side: Return 425 with Retry-After header. Configure appropriate timing windows. Implement nonce validation. Add replay protection logging.
- 5Step 5: Fix - Infrastructure: Review load balancer timing settings. Configure API gateway replay protection. Adjust WAF timing rules. Monitor network latency.
</>Code Examples
Fetch API: Handle 425 with Timing and Nonces
1// Client-side: Handle 425 by adding timing information
2async function sendRequestWithTiming(data) {
3 const timestamp = Date.now();
4 const nonce = crypto.randomUUID(); // Generate unique nonce for each request
5
6 const response = await fetch('/api/endpoint', {
7 method: 'POST',
8 headers: {
9 'Content-Type': 'application/json',
10 'X-Request-Timestamp': timestamp.toString(),
11 'X-Request-Nonce': nonce,
12 },
13 body: JSON.stringify(data),
14 });
15
16 if (response.status === 425) {
17 // Request too early - wait for Retry-After
18 const retryAfter = parseInt(response.headers.get('Retry-After') || '1');
19 console.log(`Request too early, waiting ${retryAfter} seconds...`);
20
21 await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
22
23 // Retry with updated timestamp and new nonce
24 return sendRequestWithTiming(data);
25 }
26
27 return response.json();
28}
29
30// Prevent rapid retries that trigger 425
31class RequestThrottler {
32 constructor(minDelay = 1000) {
33 this.minDelay = minDelay;
34 this.lastRequestTime = 0;
35 }
36
37 async throttle(fn) {
38 const now = Date.now();
39 const timeSinceLastRequest = now - this.lastRequestTime;
40 const delay = Math.max(0, this.minDelay - timeSinceLastRequest);
41
42 if (delay > 0) {
43 await new Promise(resolve => setTimeout(resolve, delay));
44 }
45
46 this.lastRequestTime = Date.now();
47 return fn();
48 }
49}
50
51const throttler = new RequestThrottler(1000); // 1 second minimum between requests
52await throttler.throttle(() => sendRequestWithTiming(data));Express.js: Replay Attack Prevention
1// Server-side: Implement replay attack prevention
2const express = require('express');
3const crypto = require('crypto');
4const app = express();
5
6// Store used nonces (in production, use Redis)
7const usedNonces = new Set();
8const NONCE_TTL = 5 * 60 * 1000; // 5 minutes
9
10// Clean up old nonces periodically
11setInterval(() => {
12 // In production, use Redis TTL instead
13 usedNonces.clear(); // Simplified - should track timestamps
14}, NONCE_TTL);
15
16// Replay protection middleware
17const preventReplay = (req, res, next) => {
18 const timestamp = parseInt(req.headers['x-request-timestamp'] || '0');
19 const nonce = req.headers['x-request-nonce'];
20 const now = Date.now();
21
22 // Check if nonce is provided
23 if (!nonce) {
24 return res.status(425)
25 .set('Retry-After', '1')
26 .json({
27 error: 'Too Early',
28 message: 'Request must include X-Request-Nonce header',
29 });
30 }
31
32 // Check if nonce was already used (replay attack)
33 if (usedNonces.has(nonce)) {
34 return res.status(425)
35 .set('Retry-After', '60')
36 .json({
37 error: 'Too Early',
38 message: 'Request replay detected',
39 });
40 }
41
42 // Check if request is too early (within timing window)
43 const timeDiff = now - timestamp;
44 const TIMING_WINDOW = 5000; // 5 seconds
45
46 if (timeDiff < 0 || timeDiff > TIMING_WINDOW) {
47 return res.status(425)
48 .set('Retry-After', '1')
49 .json({
50 error: 'Too Early',
51 message: 'Request timing window violation',
52 timingWindow: TIMING_WINDOW,
53 timeDiff: timeDiff,
54 });
55 }
56
57 // Mark nonce as used
58 usedNonces.add(nonce);
59
60 // Clean up nonce after TTL
61 setTimeout(() => {
62 usedNonces.delete(nonce);
63 }, NONCE_TTL);
64
65 next();
66};
67
68// Apply to sensitive endpoints
69app.post('/api/payment', preventReplay, express.json(), (req, res) => {
70 // Process payment with replay protection
71 res.json({ success: true });
72});Nginx: Pass Timing Headers
1# Nginx: Pass timing headers to backend
2server {
3 listen 80;
4 server_name api.example.com;
5
6 location /api/ {
7 proxy_pass http://backend;
8 proxy_set_header Host $host;
9 proxy_set_header X-Real-IP $remote_addr;
10
11 # Pass timing headers to backend
12 proxy_set_header X-Request-Timestamp $http_x_request_timestamp;
13 proxy_set_header X-Request-Nonce $http_x_request_nonce;
14
15 # Backend handles replay protection
16 }
17}↗Related Errors
Provider Information
This error code is specific to HTTP services. For more information, refer to the official HTTP documentation.