
An S3 presigned URL grants temporary access to a specific Amazon S3 request, such as downloading a private file or uploading an object. The recipient uses the signed link instead of receiving your AWS credentials. Your application still decides who may request that link and which object they may access.
This guide covers GET downloads and PUT uploads for Amazon S3 general-purpose buckets. It includes Python examples, expiry rules, and a small acceptance checklist. The broader Amazon cloud storage guide explains how S3 differs from EBS, EFS, and FSx.
The signed request identifies an operation, bucket, object key, and validity period. S3 evaluates the signature and the signer's authorization when the request arrives. Creating a URL successfully does not prove that S3 will authorize it.
Consider an application that lets a customer download an invoice:
Do not let a client substitute an arbitrary bucket or object key without an authorization check. A valid application login alone does not establish permission to read every customer's files. Our application storage guide covers the wider service-selection and access-design decisions.
Use a private test bucket and a small, non-sensitive file. Keep public access blocked. Configure an AWS identity with only the object permissions needed for the exercise. AWS's least-privilege guidance applies to presigned requests just as it does to other S3 access.
For the download example, the signing identity needs access to the selected object through s3:GetObject. A request for a specific version uses s3:GetObjectVersion. Objects encrypted with AWS KMS require the applicable key permissions as well. Check the GetObject permission requirements for your configuration.
Install Boto3 in your Python environment. Configure credentials through an approved role, session, or profile in the SDK credential chain; do not put access keys in the script or browser code. The examples deliberately specify Signature Version 4.
Save this as presigned_demo.py. Replace the bucket placeholder, region, and key with your test values. The key must identify an existing object. ExpiresIn=300 requests a five-minute lifetime; temporary credentials or policy restrictions can shorten it.
import boto3
from botocore.config import Config
s3 = boto3.client(
"s3",
region_name="us-east-1",
config=Config(signature_version="s3v4"),
)
download_url = s3.generate_presigned_url(
"get_object",
Params={
"Bucket": "YOUR_PRIVATE_TEST_BUCKET",
"Key": "presigned-demo/sample.txt",
},
ExpiresIn=300,
)
print(download_url)
The Boto3 signing method takes the client method, its parameters, and an expiry in seconds. Run the script in a private terminal, then open the resulting URL in a browser. Keep that output out of shared logs and screenshots. For production, return it only to the authorized client rather than printing it.
If you prefer a console workflow, select the object and use Share with a presigned URL. This creates a download link; it does not make the bucket public.
For a PUT upload, the signing identity needs s3:PutObject for the destination. Choose the key on the server. Append the following example to the same script so it reuses the configured s3 client. It assigns a new key and signs the expected content type and a conditional-write header.
from uuid import uuid4
# Reuse the configured s3 client from the download example.
upload_key = f"presigned-demo/uploads/{uuid4()}.txt"
upload_url = s3.generate_presigned_url(
"put_object",
Params={
"Bucket": "YOUR_PRIVATE_TEST_BUCKET",
"Key": upload_key,
"ContentType": "text/plain",
"IfNoneMatch": "*",
},
ExpiresIn=300,
)
print(upload_url)
print("Required headers: Content-Type: text/plain; If-None-Match: *")
Send the file with an HTTP PUT client and both headers. This curl command works in a POSIX shell; on Windows PowerShell, use curl.exe if curl resolves to another command. Replace the URL placeholder locally, keeping the quotation marks:
curl --fail-with-body --request PUT --upload-file sample.txt --header "Content-Type: text/plain" --header "If-None-Match: *" "YOUR_PRESIGNED_PUT_URL"
Upload the raw file body, not a multipart form around it. Keep the content-type header identical to the signed value, as required by the S3 upload instructions. Do not change the method, path, or query parameters after signing.
A regular PutObject request can replace the object at the same key; with versioning enabled, it can create a new current version. A unique server-selected key reduces accidental collisions.
The example also signs If-None-Match: *. This conditional write fails with HTTP 412 when a current object already exists at that key. In a versioned bucket, the condition checks the current version. It does not establish a permanent one-use link: later deletion or a delete marker can change the result.
A signed content type is not proof that an uploaded file is safe or actually has that format. Before making uploads available, validate their contents and apply your application's size, ownership, and malware-handling rules. Use a separate upload area while those checks run.
According to the S3 expiry rules, a console-generated link can last up to 12 hours; CLI and SDK links can last up to seven days with suitable credentials. A URL created with temporary credentials expires when those credentials expire, even if a longer duration was requested.
Presigned URLs are reusable bearer credentials. A download started before expiry may continue afterward, but restarting it after expiry can fail. Choose a short duration that still accommodates the user's task. Expiry does not remove a file already downloaded.
For a truly one-time download, design application-level authorization and consumption tracking. Simply generating a presigned URL and marking a database row “used” does not prevent the recipient from reusing that URL while S3 still accepts it.
A browser application calling S3 from another origin may need an appropriate CORS configuration. Allow the required origin, HTTP method, and request headers. For the PUT example, account for Content-Type and If-None-Match. A command-line success does not verify browser behavior.
CORS is not a substitute for authorization. A link recipient using a non-browser client is not limited by the browser's cross-origin checks. Test both the actual browser origin and the underlying S3 permissions.
Protect the signing endpoint itself: authenticate callers, check access to the requested object, limit request rates, and avoid returning unrelated object information. Store an audit record of the requester, operation, approved object, and requested expiry without copying the complete signed URL.
Logging out of your application does not by itself invalidate an existing S3 URL. If a link is exposed, assess the object access and signing identity with your security owner. Permission changes or credential revocation can affect other clients too. Verify the resulting denial; generating a replacement link does not cancel the original.
Record the error code and request identifier for diagnosis, while redacting credentials and signatures. Reproduce the request with a small test file before changing application behavior. When policies change, assess their effect on existing clients as well as the new link.
The Python examples were checked locally with Python 3.12.14 and Boto3/Botocore 1.43.121 using dummy credentials. The check confirmed SigV4 signing, GET and PUT methods, expiry parameters, and required signed headers. It did not contact AWS or prove that a live bucket would accept the requests.
Run these acceptance checks in your own test environment:
Budget for S3 storage, requests, applicable retrievals, and data transfer, plus any application or monitoring services. Repeated use of a valid link can generate repeated requests and transfers. A signed URL changes access, not the service's charging model.
Use presigned URLs when an application needs bounded access to a particular object without giving the recipient AWS credentials. If the requirement is permanent public delivery, recipient-specific access checks on every download, or a managed file-sharing experience, evaluate a delivery or application layer designed for that requirement.
For an S3-compatible service, verify its supported operations, signature behavior, expiry, headers, and conditional writes against its own documentation. This Amazon S3 walkthrough does not establish feature support for another provider.
Pick one AI, compute, or storage workload and see the difference for yourself. Spin it up in minutes, or let our team map your fastest path to production.