Presigned URLs

A presigned URL is a regular HTTPS URL with an embedded signature that grants temporary access to a single object — no Access Key required by the recipient.

Use presigned URLs to:

  • Let a user download a private object from a browser, without giving them credentials.
  • Let a client upload a file directly to your bucket, bypassing your backend.
  • Hand a third-party system temporary access to a single file.

The URL stops working when its expiry passes.

Before you begin

The S3 endpoint for your organization is https://{workspaceId}.blob.ibeestorage.com.

Generate a download URL with the AWS CLI

$aws --endpoint-url https://{workspaceId}.blob.ibeestorage.com \
> --profile ibee \
> s3 presign s3://my-bucket/reports/2026-05.pdf \
> --expires-in 3600

The command prints a URL with embedded signature parameters:

https://{workspaceId}.blob.ibeestorage.com/my-bucket/reports/2026-05.pdf?
X-Amz-Algorithm=AWS4-HMAC-SHA256
&X-Amz-Credential=...
&X-Amz-Date=...
&X-Amz-Expires=3600
&X-Amz-SignedHeaders=host
&X-Amz-Signature=...

Anyone with the URL can GET the object until the expiry passes.

Generate from an SDK

1from datetime import timedelta
2import boto3
3
4s3 = boto3.client(
5 "s3",
6 endpoint_url="https://{workspaceId}.blob.ibeestorage.com",
7 aws_access_key_id="AKIA...",
8 aws_secret_access_key="<your-secret-access-key>",
9)
10
11url = s3.generate_presigned_url(
12 "get_object",
13 Params={"Bucket": "my-bucket", "Key": "reports/2026-05.pdf"},
14 ExpiresIn=int(timedelta(hours=1).total_seconds()),
15)
16print(url)

Presigned uploads (PUT)

Generate a URL the client can PUT to directly:

1url = s3.generate_presigned_url(
2 "put_object",
3 Params={
4 "Bucket": "my-bucket",
5 "Key": "uploads/avatar.png",
6 "ContentType": "image/png",
7 },
8 ExpiresIn=600,
9)

The client uploads with a single HTTP request:

$curl -X PUT "$URL" \
> -H "Content-Type: image/png" \
> --data-binary @avatar.png

Presigned POST (browser uploads with form data)

POST policies are the right fit for browser-based form uploads — they let you constrain the upload (key prefix, max size, content type) before the file is sent.

1post = s3.generate_presigned_post(
2 Bucket="my-bucket",
3 Key="uploads/${filename}",
4 Conditions=[
5 ["content-length-range", 0, 10 * 1024 * 1024], # 10 MB max
6 ["starts-with", "$Content-Type", "image/"],
7 ],
8 ExpiresIn=600,
9)
10print(post) # url + fields

Best practices

  • Use the shortest expiry that works. Don’t issue a long-lived URL when 5 minutes is enough.
  • Bind to content type and size for upload URLs to prevent abuse.
  • Don’t log presigned URLs — anyone who reads the log gets the same access.
  • Treat them as one-time secrets — regenerate per request rather than caching.

Limitations

  • The URL stops working if the API token used to sign it is revoked.
  • Each presigned URL is valid for one HTTP method on one key. Different operations need different URLs.