How can I create a one time download link with Amazon S3?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Amazon S3 can generate presigned download URLs, but a presigned URL is time-limited, not truly one-time. If you need “download exactly once,” S3 alone is not enough. You need an application-side tracking step or a post-download invalidation strategy layered on top of the presigned URL.
What a Presigned URL Actually Gives You
A presigned URL lets someone perform a specific S3 operation, such as GetObject, without having AWS credentials. The URL is signed with an expiration time.
This is useful for short-lived downloads, but anyone who has the URL can typically use it multiple times until it expires.
Why It Is Not One-Time by Default
S3 does not keep built-in state such as “this presigned URL has already been consumed once.” The signature only proves that the request is valid for a certain object and time window.
So if the same recipient opens the link twice before expiration, both requests can succeed unless you add extra logic.
Pattern 1: App Endpoint That Issues the Real Download Once
A common design is:
- user requests a download through your application
- your app checks whether the token is still unused
- your app generates a short-lived presigned URL or streams the file
- your app marks the token as consumed
That way, the one-time rule lives in your database or application state, not in S3 itself.
In real systems, you would store this state in a durable data store rather than an in-memory set.
Pattern 2: Delete or Move the Object After First Download
Another possible pattern is to let the first successful request trigger deletion or movement of the object. This is more fragile because S3 itself does not execute “delete after first GET” as an atomic built-in feature. You would need application logic or event-driven automation, and race conditions become possible if multiple requests arrive close together.
For that reason, deletion-after-download is usually less reliable than explicit application-level token tracking.
Pattern 3: Short Expiration Plus Server Tracking
A pragmatic compromise is:
- generate a very short-lived presigned URL
- wrap it behind a one-time token in your app
- invalidate the token immediately after first use
This minimizes exposure if the URL leaks and gives you a real one-time control plane outside S3.
Security Notes
Even for normal temporary sharing, keep the expiration window short and scope the URL to exactly one object.
Also remember:
- anyone with the URL can use it until it expires
- browser prefetching or email scanners can accidentally consume URLs if your one-time workflow is too naive
- audit logging is your responsibility if you need to know who used the link
Common Pitfalls
A common mistake is assuming “presigned” means “single-use.” It does not. Another is trying to force one-time behavior entirely inside S3 without any application state. Developers also often underestimate the effect of link scanners, which can hit the URL before the intended human user does. Finally, deleting the object immediately after one request can still fail to enforce one-time semantics under concurrent access unless the workflow is carefully designed.
Summary
- A presigned S3 URL is time-limited, not inherently one-time.
- True one-time download behavior requires application-side tracking or another stateful control layer.
- The cleanest design is usually a one-time token in your app that issues a short-lived presigned URL only once.
- Deleting the object after the first download is possible but operationally weaker.
- Keep expirations short and remember that anyone holding the URL can reuse it until it expires.

