Idempotency vs Nonce: Two Weapons Against Duplicate Requests

Search for a command to run...

No comments yet. Be the first to comment.
Everything so far happens inside one conversation. Professional usage means building infrastructure around conversations: persistent context, standing instructions, and live connections to your actual

Chat answers evaporate. Artifacts persist: documents, code, diagrams, and fully working apps that appear in a panel beside the conversation, get iterated like real work products, and can be downloaded

Typed prompts only scratch the surface. Claude's practical power multiplies when you feed it your material — documents, screenshots, spreadsheets — and this is where the hallucination problem from Par

Prompting is the highest-leverage skill in this entire series. We'll build it in three levels — start where you are. Level 1: The foundation formula Every solid prompt covers four things: Role + Task

Signing up takes two minutes. What separates casual users from effective ones isn't the interface — it's conversation mechanics: how you manage context, when you branch, when you restart, and which se

Every backend developer eventually faces this scenario: a user clicks "Pay" twice, a network timeout causes a client to retry, or a message queue delivers the same event more than once. These situations can create duplicate orders, double charges, or corrupted data.
Two battle-tested strategies exist to defend against this:
Idempotency and Nonce. They look similar on the surface, but they serve different purposes. Let's break them down.
Before diving into solutions, let's understand the enemy.
Client ──── POST /pay ────► Server
Client ◄──── timeout ────── Server (processing...)
Client ──── POST /pay ────► Server ← 💣 DUPLICATE!
The client never got a response, so it retried — but the first request was already processed. Now you have two charges.
This happens in:
Mobile apps on unstable connections
Message queues (at-least-once delivery)
Retry logic in service-to-service communication
Frontend double-clicks
An operation is idempotent if calling it once or calling it many times produces the same result.
f(f(x)) = f(x)
Think of a light switch that only turns on: pressing it once or ten times — the light is on. Same result.
In HTTP terms: GET, PUT, and DELETE are naturally idempotent. POST is not — that's where you need to enforce it yourself.
The client generates a unique Idempotency Key and sends it with every request. The server stores the result of the first execution. On any subsequent request with the same key, the server returns the cached result without re-executing the operation.
Request 1: POST /pay {idempotency-key: "abc-123"} → Processed ✅ (result saved)
Request 2: POST /pay {idempotency-key: "abc-123"} → Returned from cache ✅ (not re-executed)
Request 3: POST /pay {idempotency-key: "abc-123"} → Returned from cache ✅ (not re-executed)
The key should be generated by the client (usually a UUID v4).
Store idempotency records in a persistent store (DB or Redis), not in memory.
Set a TTL on records (e.g., 24 hours or 7 days) — you don't need them forever.
Handle concurrent requests with the same key using a database unique constraint or distributed lock.
Nonce stands for "Number used Once". It's a unique token that is valid for a single use only. Once consumed, it's permanently invalidated — even if the operation behind it failed.
The key difference from idempotency: a nonce doesn't cache results. It just asks: "Has this token been used before?"
Request 1: POST /transfer {nonce: "xyz-789"} → Valid, consumed ✅
Request 2: POST /transfer {nonce: "xyz-789"} → REJECTED ❌ (already used)
Even if request 1 failed for some business reason, request 2 with the same nonce is still rejected. The client must generate a new nonce for a new attempt.
You can use Redis SETNX (SET if Not eXists) for atomic check-and-consume. Never do a separate GET then SET — that's a race condition.
Set a reasonable TTL based on your use case (5–15 minutes for form submissions, longer for payment flows).
The client must generate a new nonce for every new attempt. Nonces are not reusable.
Nonces work great for one-time form submissions, OTP verification, and CSRF protection.
| Idempotency | Nonce | |
|---|---|---|
| Core question | "Has this operation been done before?" | "Has this token been used before?" |
| On duplicate request | Returns cached result | Rejects with error |
| Client retry behavior | Safe to retry with same key | Must generate a new nonce |
| Stores | Full request result | Just the token |
| Best for | Payment APIs, order creation, idempotent POST endpoints | Form submissions, OTPs, CSRF, single-use links |
| Response on duplicate | 200 OK (cached) |
409 Conflict |
| Key generated by | Client | Client (or server pre-issues it) |
| TTL | Hours to days | Minutes |
Is the client allowed to retry with the same intent?
│
├── YES → Use Idempotency
│ (same operation, same result expected)
│ Example: "Charge $100 for order #456"
│
└── NO → Use Nonce
(one shot only, new attempt = new token)
Example: "Submit this form", "Verify this OTP"
Real-world rule of thumb:
Payment APIs → Idempotency (Stripe, PayPal both use this)
Authentication flows, form submissions → Nonce
CSRF protection → Nonce
Webhook delivery → Idempotency
In high-security systems, you can use both together. This gives you the best of both worlds: replay protection in the short term (nonce) and safe retries over time (idempotency).
| Concept | One-liner |
|---|---|
| Idempotency | Same key → same result, always safe to retry |
| Nonce | Single-use token → rejected on second use |
Both patterns are essential tools in any production backend. Idempotency is your friend when you need fault-tolerant retry logic. Nonce is your guard against replay attacks and accidental double submissions.
Once you start thinking in these terms, you'll spot the need for them everywhere.