The Problem: Private Buckets, Public Downloads
By default, S3 buckets should be private. Making a bucket public to share a file is a security anti-pattern — it exposes every object in the bucket to anyone on the internet. But how do you let a specific authenticated user download a premium video, or let a customer upload their own document, without making the bucket public?
How Presigned URLs Work
A Presigned URL is a time-limited URL generated by your application using the AWS SDK. The URL cryptographically encodes the identity of the IAM principal that generated it, the specific S3 object it grants access to, the HTTP method allowed (GET for download, PUT for upload), and an expiration timestamp.
When a user makes a request with this URL, S3 validates the embedded signature and expiration. If valid, S3 serves the request as if the generating IAM principal made it directly. The user needs no AWS credentials of their own.
User → App Server: "I want to download invoice_2024_01.pdf"
App Server → AWS SDK: generate_presigned_url(bucket, key, expiry=900)
AWS SDK → App Server: "https://bucket.s3.amazonaws.com/invoice_2024_01.pdf?X-Amz-Signature=..."
App Server → User: [presigned URL]
User → S3: GET https://bucket.s3.amazonaws.com/invoice_2024_01.pdf?X-Amz-Signature=...
S3 → User: [file contents, directly]
Benefits
- Security: The bucket remains private. The URL is scoped to a single object and expires automatically.
- Performance: The user downloads the file directly from S3, not through your application server. This offloads bandwidth and CPU from your app tier.
- Upload use case: Presigned
PUTURLs allow users to upload files directly to S3 without your server acting as a proxy. Your server generates the URL, the user uploads directly, and you receive an event notification (via S3 Event Notifications or EventBridge) when the upload completes.
Expiration considerations: The maximum expiration for a presigned URL generated with IAM role credentials (the most common case) is 12 hours (the maximum session duration of the role). For IAM user credentials, the maximum is 7 days. Design your application to generate URLs on demand rather than caching them.