Configuring Request Replay and Idempotency
1. Implementing Idempotency Keys
Example: Idempotent POST
POST /v1/payments HTTP/1.1
Idempotency-Key: a1b2c3d4-5678-90ab-cdef-1234567890ab
Content-Type: application/json
{"amount": 1000, "currency": "USD"}
| Property | Value |
|---|---|
| Format | UUID v4 or random ≥ 16 bytes |
| Scope | Per-client / per-endpoint |
| Storage TTL | 24 hours (Stripe), 7 days (some) |
| Response | Replay cached result on duplicate |
2. Configuring Idempotent Methods
| Method | Idempotent? |
|---|---|
| GET, HEAD, OPTIONS | Yes (safe) |
| PUT | Yes |
| DELETE | Yes |
| POST | No (need Idempotency-Key) |
| PATCH | Usually no |
3. Using Request Deduplication
| Dedup Source | Notes |
|---|---|
| Idempotency-Key header | Explicit client opt-in |
| Body fingerprint | sha256 of canonical body |
| Natural key | Order ID, txn ID |
| Replay window | Slow expire to catch retries |
4. Setting Idempotency Window
| Provider | Window |
|---|---|
| Stripe | 24 hours |
| PayPal | 72 hours |
| AWS | 10 min (some APIs) |
| Recommended | ≥ max retry duration (often 24h) |
5. Implementing Idempotency Storage
Example: Redis idempotency check
String key = "idem:" + clientId + ":" + idemKey;
String existing = redis.get(key);
if (existing != null) {
return CachedResponse.from(existing); // replay
}
// Lock to avoid concurrent execution
if (!redis.set(key + ":lock", "1", "NX", "EX", 60)) {
return Response.status(409).entity("In progress").build();
}
Response r = execute();
redis.setex(key, 86400, serialize(r));
return r;
6. Using Conditional Requests
| Header | Behavior |
|---|---|
If-Match | Update only if ETag matches |
If-None-Match | Create only if not exists (*) |
If-Modified-Since | 304 if not modified |
If-Unmodified-Since | 412 if changed |
7. Configuring Replay Protection
| Mechanism | Purpose |
|---|---|
| Nonce | One-time random value |
| Timestamp | Reject if > 5 min skew |
| Signed request | HMAC includes timestamp+nonce |
| Used-nonce cache | Reject duplicates within window |
8. Implementing Request Fingerprinting
Example: Canonical body hash
String canonical = JsonCanonicalizer.canonicalize(body);
String fingerprint = sha256Hex(method + path + canonical);
9. Setting Up Replay Attack Prevention
| Check | Reject If |
|---|---|
| Timestamp window | |now - req_time| > 300s |
| Nonce seen | Already in nonce cache |
| Signature invalid | HMAC mismatch |
| TLS session | Not from current session |
10. Configuring Idempotency Headers
| Header | Direction |
|---|---|
Idempotency-Key | Request (client) |
Idempotency-Replayed: true | Response (server) |
Idempotency-Expiry | Response |