Amazon Simple Storage Service (Amazon S3)
AWS S3 Advanced Features
Presigned URLs
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.
Why Use Pre-Signed URLs?
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.
How Pre-Signed URLs Work
- Generate URL: Your backend calls the S3 API to create a pre-signed URL, specifying the bucket, object key, operation (
get_object
orput_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.
Use Case: Secure Video Streaming
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:
import boto3
from botocore.exceptions import ClientError
def create_presigned_get_url(bucket_name, object_key, expiration=3600):
s3 = boto3.client('s3')
try:
return s3.generate_presigned_url(
'get_object',
Params={'Bucket': bucket_name, 'Key': object_key},
ExpiresIn=expiration
)
except ClientError as e:
print(f"Error generating GET URL: {e}")
return None
# Example usage
url = create_presigned_get_url(
bucket_name='my-video-bucket',
object_key='videos/movie.mp4',
expiration=600
)
print(url)
Use Case: Direct Client Uploads
By default, clients upload files through your backend (EC2), which consumes bandwidth and CPU:
With a pre-signed PUT URL, clients upload directly to S3:
import boto3
from botocore.exceptions import ClientError
def create_presigned_put_url(bucket_name, object_key, expiration=3600):
s3 = boto3.client('s3')
try:
return s3.generate_presigned_url(
'put_object',
Params={'Bucket': bucket_name, 'Key': object_key},
ExpiresIn=expiration
)
except ClientError as e:
print(f"Error generating PUT URL: {e}")
return None
# Example usage
url = create_presigned_put_url(
bucket_name='my-bucket',
object_key='uploads/profile-pic.png',
expiration=300
)
print(url)
Note
Bypassing your backend for large file uploads reduces latency and operational costs.
Expiration and Permissions
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:
Warning
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.
References
Watch Video
Watch video content