TLS stops someone from reading or tampering with your app's requests in transit, but it does not stop a captured request from being sent again. If an attacker, or a malicious client, gets hold of a valid request, a payment, a state change, a one-time code, and replays it, the server may happily perform the action a second time. That is a replay attack, and the defense is to make each sensitive request usable exactly once: a nonce the server tracks, a timestamp window, an idempotency key. Here is what replay attacks are and how to protect a mobile API against them.
Short answer
A replay attack is when an attacker captures a valid request, token, or message and sends it again to repeat the action it performed, such as re-submitting a payment, reusing a one-time code, or replaying a signed request. Per OWASP, TLS prevents eavesdropping but not replay by a party who has the request, so sensitive operations need explicit replay protection: a nonce (a unique, unpredictable value the server records and rejects if reused), a timestamp with a short validity window, and idempotency keys for operations that must not repeat. These are enforced on the server, since the app is one client and the server is what decides whether to honor a request. Make each sensitive request usable exactly once.
What you should know
- A replay re-sends a valid request: to repeat the action it performed.
- TLS does not prevent it: encryption stops reading, not resending.
- Sensitive operations are the target: payments, codes, state changes.
- A nonce makes a request single-use: the server records and rejects reuse.
- Server-side enforcement: the server decides whether to honor a request.
What is a replay attack?
It is reusing a captured valid request to repeat its effect. An attacker who obtains a legitimate request, by capturing it, by being a malicious client, or by getting a token or message, sends it again, and if the server processes it the same way, the action happens twice: a payment is charged again, a one-time code is accepted a second time, a state-changing request is repeated. The point is that the request is genuinely valid, it was authentic the first time, so the server cannot reject it on the grounds of being malformed or unauthenticated; the problem is solely that it is being used more than once. TLS does not help here, since it protects the request from being read or altered in transit but does nothing to stop a party who already has the request from sending it again. So replay protection has to make a sensitive request distinguishable as already-used.
What are the defenses?
A few mechanisms that make a request single-use or time-bound. The table lists them.
| Defense | How it works |
|---|---|
| Nonce | A unique value per request the server records and rejects if reused |
| Timestamp window | Reject requests older than a short validity period |
| Idempotency key | A client-supplied key the server dedupes so an operation runs once |
| One-time tokens | Codes and reset tokens accepted exactly once |
| Sequence numbers | Monotonic counters that reject out-of-order reuse |
A nonce, a number used once, is the core mechanism: the client includes a unique, unpredictable value with each sensitive request, and the server records which nonces it has seen and rejects any repeat, so a replayed request fails because its nonce is already spent. A short timestamp window bounds how long any request is valid, so even an unseen replay is rejected if it is stale, and it keeps the nonce-tracking set manageable. Idempotency keys handle operations that must not repeat, like payments: the client sends a unique key, and the server performs the operation once per key, returning the same result for retries instead of repeating it. One-time tokens and sequence numbers apply the same idea to codes and ordered messages.
How do you add replay protection?
Make sensitive requests single-use and time-bound, enforced on the server. For requests that perform sensitive or state-changing actions, have the client include a nonce and a timestamp, and on the server reject any request whose timestamp is outside a short window or whose nonce has already been seen, recording nonces for the validity window so reuse is caught. For operations that must not repeat, such as payments or order submission, use idempotency keys so the server performs the action once per key and safely deduplicates retries. Make one-time codes and reset tokens genuinely single-use, invalidating them on first use. Where requests are signed, include the nonce and timestamp in the signed content so they cannot be altered. And keep all of this server-side, since the app is one client and an attacker can resend a request directly; the server is the only place that can refuse a second use. The principle is that a sensitive request should be honored exactly once, and the server enforces that.
What to watch out for
The first trap is assuming TLS prevents replay, when it only prevents reading and tampering, not resending; sensitive actions need their own protection. The second is non-idempotent operations like payments without idempotency keys, where a retry or replay charges twice. The third is one-time codes or tokens that are not actually invalidated after use, letting them be replayed. Replay protection is server-side logic, so a pre-submission scan such as PTKD.com (https://ptkd.com), which reads the binary against OWASP MASVS, surfaces the API endpoints and authentication your app uses as the surface to protect, while the nonce, timestamp, and idempotency handling are yours to implement on the backend.
What to take away
- A replay attack resends a captured valid request to repeat its action, and TLS does not prevent it, since encryption stops reading, not resending.
- Defend sensitive operations with a nonce the server records and rejects on reuse, a short timestamp window, and idempotency keys for operations that must not repeat.
- Make one-time codes and tokens genuinely single-use, include nonces and timestamps in signed content, and enforce all of it on the server.
- Use a pre-submission scan such as PTKD.com to surface the endpoints and authentication your app uses, then add replay protection server-side.

