Presigned URLs
Presigned URLs are an S3 concept ↗ for granting temporary access to objects without exposing your API credentials. A presigned URL includes signature parameters in the URL itself, authorizing anyone with the URL to perform a specific operation (like GetObject or PutObject) on a specific object until the URL expires.
They are ideal for granting temporary access to specific objects, such as allowing users to upload files directly to R2 or providing time-limited download links.
To generate a presigned URL, you specify:
- Resource identifier: Account ID, bucket name, and object path
- Operation: The S3 API operation permitted (GET, PUT, HEAD, or DELETE)
- Expiry: Timeout from 1 second to 7 days (604,800 seconds)
Presigned URLs are generated client-side with no communication with R2, requiring only your R2 API credentials and an implementation of the AWS Signature Version 4 signing algorithm.
- Account ID (for constructing the S3 endpoint URL)
- R2 API token (Access Key ID and Secret Access Key)
- AWS SDK or compatible S3 client library
import { S3Client, GetObjectCommand, PutObjectCommand } from "@aws-sdk/client-s3";import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
const S3 = new S3Client({ region: "auto", // Required by SDK but not used by R2 // Provide your Cloudflare account ID endpoint: `https://<ACCOUNT_ID>.r2.cloudflarestorage.com`, // Retrieve your S3 API credentials for your R2 bucket via API tokens (see: https://developers.cloudflare.com/r2/api/tokens) credentials: { accessKeyId: '<ACCESS_KEY_ID>', secretAccessKey: '<SECRET_ACCESS_KEY>', },});
// Generate presigned URL for reading (GET)const getUrl = await getSignedUrl( S3, new GetObjectCommand({ Bucket: "my-bucket", Key: "image.png" }), { expiresIn: 3600 }, // Valid for 1 hour);// https://my-bucket.<ACCOUNT_ID>.r2.cloudflarestorage.com/image.png?X-Amz-Algorithm=...
// Generate presigned URL for writing (PUT)// Specify ContentType to restrict uploads to a specific file typeconst putUrl = await getSignedUrl( S3, new PutObjectCommand({ Bucket: "my-bucket", Key: "image.png", ContentType: "image/png", }), { expiresIn: 3600 },);import boto3
s3 = boto3.client( service_name="s3", # Provide your Cloudflare account ID endpoint_url='https://<ACCOUNT_ID>.r2.cloudflarestorage.com', # Retrieve your S3 API credentials for your R2 bucket via API tokens (see: https://developers.cloudflare.com/r2/api/tokens) aws_access_key_id='<ACCESS_KEY_ID>', aws_secret_access_key='<SECRET_ACCESS_KEY>', region_name="auto", # Required by SDK but not used by R2)
# Generate presigned URL for reading (GET)get_url = s3.generate_presigned_url( 'get_object', Params={'Bucket': 'my-bucket', 'Key': 'image.png'}, ExpiresIn=3600 # Valid for 1 hour)# https://my-bucket.<ACCOUNT_ID>.r2.cloudflarestorage.com/image.png?X-Amz-Algorithm=...
# Generate presigned URL for writing (PUT)# Specify ContentType to restrict uploads to a specific file typeput_url = s3.generate_presigned_url( 'put_object', Params={ 'Bucket': 'my-bucket', 'Key': 'image.png', 'ContentType': 'image/png' }, ExpiresIn=3600)# Generate presigned URL for reading (GET)# The AWS CLI presign command defaults to GET operationsaws s3 presign --endpoint-url https://<ACCOUNT_ID>.r2.cloudflarestorage.com \ s3://my-bucket/image.png \ --expires-in 3600
# Output:# https://<ACCOUNT_ID>.r2.cloudflarestorage.com/my-bucket/image.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...
# Note: The AWS CLI presign command only supports GET operations.# For PUT operations, use one of the SDK examples above.For complete examples and additional operations, refer to the SDK-specific documentation:
When generating presigned URLs, you can limit abuse and misuse by:
- Restricting Content-Type: Specify the allowed
Content-Typein your SDK's parameters. The signature will include this header, so uploads will fail with a403/SignatureDoesNotMatcherror if the client sends a differentContent-Typefor an upload request. - Configuring CORS: If your presigned URLs will be used from a browser, set up CORS rules on your bucket to control which origins can make requests.
Once generated, use a presigned URL like any HTTP endpoint. The signature is embedded in the URL, so no additional authentication headers are required.
# Download using a GET presigned URLcurl "https://my-bucket.<ACCOUNT_ID>.r2.cloudflarestorage.com/image.png?X-Amz-Algorithm=..."
# Upload using a PUT presigned URLcurl -X PUT "https://my-bucket.<ACCOUNT_ID>.r2.cloudflarestorage.com/image.png?X-Amz-Algorithm=..." \ --data-binary @image.pngYou can also use presigned URLs directly in web browsers, mobile apps, or any HTTP client. The same presigned URL can be reused multiple times until it expires.
The following is an example of a presigned URL that was created using R2 API credentials and following the AWS Signature Version 4 signing process:
https://my-bucket.123456789abcdef0123456789abcdef.r2.cloudflarestorage.com/photos/cat.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=CFEXAMPLEKEY12345%2F20251201%2Fauto%2Fs3%2Faws4_request&X-Amz-Date=20251201T180512Z&X-Amz-Expires=3600&X-Amz-Signature=8c3ac40fa6c83d64b4516e0c9e5fa94c998bb79131be9ddadf90cefc5ec31033&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObjectIn this example, this presigned url performs a GetObject on the object photos/cat.png within bucket my-bucket in the account with id 123456789abcdef0123456789abcdef. The key signature parameters that compose this presigned URL are:
X-Amz-Algorithm: Identifies the algorithm used to sign the URL.X-Amz-Credential: Contains information about the credentials used to calculate the signature.X-Amz-Date: The date and time (in ISO 8601 format) when the signature was created.X-Amz-Expires: The duration in seconds that the presigned URL remains valid, starting fromX-Amz-Date.X-Amz-Signature: The signature proving the URL was signed using the secret key.X-Amz-SignedHeaders: Lists the HTTP headers that were included in the signature calculation.
R2 supports presigned URLs for the following HTTP methods:
GET: Fetch an object from a bucketHEAD: Fetch an object's metadata from a bucketPUT: Upload an object to a bucketDELETE: Delete an object from a bucket
POST (multipart form uploads via HTML forms) is not currently supported.
Treat presigned URLs as bearer tokens. Anyone with the URL can perform the specified operation until it expires. Share presigned URLs only with intended recipients and consider using short expiration times for sensitive operations.
Presigned URLs work with the S3 API domain (<ACCOUNT_ID>.r2.cloudflarestorage.com) and cannot be used with custom domains.
If you need authentication with R2 buckets accessed via custom domains (public buckets), use the WAF HMAC validation feature (requires Pro plan or above).
Was this helpful?
- Resources
- API
- New to Cloudflare?
- Directory
- Sponsorships
- Open Source
- Support
- Help Center
- System Status
- Compliance
- GDPR
- Company
- cloudflare.com
- Our team
- Careers
- © 2025 Cloudflare, Inc.
- Privacy Policy
- Terms of Use
- Report Security Issues
- Trademark
-