TracksSpecializations and Deep DivesCloud Storage and CDNsSigned URLs for Secure Access(3 of 6)

Signed URLs for Secure Access

Making objects public is simple but often wrong. User documents, private uploads, and sensitive files shouldn't be accessible to everyone. Signed URLs solve this elegantly — they provide temporary, controlled access to private objects without exposing your storage credentials.

Why Signed URLs Matter

The traditional approach to serving private files involves your server downloading from storage, then sending to the user. This wastes bandwidth and server resources. With signed URLs, users download directly from storage, but only with your permission.

Here's the flow: a user requests a file, your server verifies they're authorized, generates a signed URL, and returns it. The user's browser then downloads directly from storage. The URL works only for a limited time and only for that specific object.

Generating Download URLs

Creating a signed download URL is straightforward:

url = s3.generate_presigned_url('get_object',
    Params={
        'Bucket': 'my-bucket',
        'Key': f'user/{user_id}/document.pdf'
    },
    ExpiresIn=300  # 5 minutes
)
# Return this URL to the client

The ExpiresIn parameter controls how long the URL remains valid. Shorter times are more secure — five minutes is usually plenty for a download to start.

Enabling Direct Uploads

Signed URLs also enable direct uploads from browsers, bypassing your server entirely. This dramatically reduces server load for file uploads:

from uuid import uuid4

url = s3.generate_presigned_url('put_object',
    Params={
        'Bucket': 'my-bucket',
        'Key': f'uploads/{uuid4()}.jpg',
        'ContentType': 'image/jpeg'
    },
    ExpiresIn=300
)

Your frontend receives this URL and uploads directly:

await fetch(signedUrl, {
    method: 'PUT',
    body: file,
    headers: { 'Content-Type': 'image/jpeg' }
});

The upload goes straight to storage. Your server never handles the file data — just the authorization decision.

Security Best Practices

Keep expiration times short. Five to fifteen minutes handles most use cases. Longer times increase the window for URL leakage.

Validate before generating. Always verify the user has permission to access or upload the specific file. Don't generate URLs blindly based on user input.

Avoid logging signed URLs. They contain sensitive signatures. If URLs appear in logs, anyone with log access gains file access.

Use unique keys for uploads. Generate random filenames (like UUIDs) to prevent users from overwriting each other's files.

See More

Further Reading

You need to be signed in to leave a comment and join the discussion