Pre-signed URLs provide secure, temporary access to private Amazon S3 objects without sharing AWS credentials.
Pre-signed URLs are a powerful Amazon S3 feature that let you grant time-limited access to private objects without sharing AWS credentials. By embedding your IAM user’s credentials and permissions into a URL, you can allow third parties to download or upload files directly to your S3 bucket. This approach keeps your bucket private and eliminates the need to proxy large files through your backend.
Imagine you have a private S3 bucket managed by an IAM-authenticated user. That user can list, upload, and download objects, but external (public) users cannot:
Two common—but suboptimal—alternatives are:
Creating an AWS account for every external user (not scalable)
Making the bucket public (exposes all objects)
Instead, generate a pre-signed URL that allows the public user to perform a single action (GET or PUT) before it expires. S3 processes the request as if it came from your IAM user.
Generate URL: Your backend calls the S3 API to create a pre-signed URL, specifying the bucket, object key, operation (get_object or put_object), and expiration.
Share URL: Send the URL to the client (public user).
Perform Action: The client uses the URL to upload or download directly from S3.
Automatic Validation: S3 verifies the signature, ensures the URL hasn’t expired, and checks permissions.
A streaming service stores videos in S3. When a paying customer requests a video, the backend issues a pre-signed GET URL so the client can stream directly from S3:
Every pre-signed URL requires an expiration time. For IAM user credentials, the maximum is 7 days (604,800 seconds). Always choose the shortest practical duration.
Operation
Required Permission
Max Expiration
get_object
s3:GetObject
7 days
put_object
s3:PutObject
7 days
Even if an IAM user lacks direct access to the bucket, they can still generate a pre-signed URL. S3 will enforce the embedded permissions:
A pre-signed URL grants the specified action to anyone holding it. Never expose URLs in public repos or client-side code that can be easily inspected.