September 30, 2026 · 5 min read
How to share private S3 and R2 files with presigned URLs
Not every file should sit in a public bucket. Contracts, invoices or unreleased builds can stay private and still be shared, with a link that only works for a limited time.
How presigned URLs work
The URL contains everything S3 needs to check the request: the key it was signed with, when it was signed, how long it's valid and a signature. It looks like this:
https://your-bucket.s3.eu-central-1.amazonaws.com/contract.pdf
?X-Amz-Algorithm=AWS4-HMAC-SHA256
&X-Amz-Credential=AKIA…/20260930/eu-central-1/s3/aws4_request
&X-Amz-Date=20260930T120000Z
&X-Amz-Expires=3600
&X-Amz-SignedHeaders=host
&X-Amz-Signature=…
Signing happens on your computer. Nothing is sent to the provider until someone opens the link, and changing any part of it breaks the signature.
Create one with the AWS CLI
aws s3 presign s3://your-bucket/contract.pdf --expires-in 86400
That link works for 24 hours. For Cloudflare R2 or another S3-compatible service, point the CLI at its endpoint:
aws s3 presign s3://your-bucket/contract.pdf --expires-in 86400 \
--endpoint-url https://<ACCOUNT_ID>.r2.cloudflarestorage.comLimits to know
- 7 days at most. Signature Version 4 allows up to 604,800 seconds, on S3 and R2 alike.
- Temporary credentials end earlier. A URL signed with an STS or SSO session stops working when that session expires, even if you asked for longer.
- R2 needs the API domain. Presigned URLs on R2 work with
<ACCOUNT_ID>.r2.cloudflarestorage.com, not with a custom domain. - Anyone with the link can use it. Treat it like a password until it expires.
- No per-link revoke. To cut off a link early, delete or rename the object, or deactivate the access key that signed it.
Presigned URL or public bucket?
Use a public bucket for files that are meant to be seen and should keep working: screenshots in docs, images in a README. Use presigned URLs for anything sensitive or short-lived. Many people keep one bucket of each and choose per file.
Common questions
Can a presigned URL last longer than 7 days?
Not with Signature Version 4, which S3 and R2 use. For longer access, make the file public or generate a new link when the old one expires.
Why does my presigned URL return SignatureDoesNotMatch?
Usually the URL was changed after signing, for example by an email client or by switching the host to a custom domain. Region and endpoint settings must also match the bucket.
Do presigned URLs work with Backblaze B2 and MinIO?
Yes. Both support Signature Version 4 presigned URLs through their S3-compatible APIs.