Introduction
An S3 presigned URL is valid only for a specific HTTP method, object key, region, credential set, and time window. If any of those inputs change between signing and request execution, S3 rejects the request with SignatureDoesNotMatch, AccessDenied, or an expiration error. The most reliable fix is to compare the exact request being made against the exact request that was signed.
Symptoms
- S3 returns
SignatureDoesNotMatch - The client sees
Request has expiredorAccessDenied - The URL works immediately after generation but fails after proxying or browser upload retries
- Downloads succeed while uploads fail, or vice versa
Common Causes
- The client signs with the wrong AWS region for the bucket
- The request method, headers, or content type differ from what was signed
- The expiration window is too short for the actual client flow
- Clock skew on the signing host or client invalidates the timestamp
Step-by-Step Fix
- 1.Confirm the exact method and object details used when generating the URL
- 2.A URL signed for
GETwill not work forPUT, and a different key or bucket region invalidates the signature immediately.
```python import boto3
s3 = boto3.client("s3", region_name="us-east-1") url = s3.generate_presigned_url( "get_object", Params={"Bucket": "my-bucket", "Key": "file.txt"}, ExpiresIn=3600, ) ```
- 1.Verify the bucket region matches the signing client region
- 2.Presigned URLs are region-sensitive. A bucket in
eu-west-1signed with anus-east-1client often fails with a signature mismatch.
aws s3api get-bucket-location --bucket my-bucket- 1.Check whether the client changed signed headers or content type
- 2.Upload flows often break because the browser or proxy adds a different
Content-Type, host, or signed header set than the presigner expected.
curl -v -X PUT \
-H "Content-Type: image/jpeg" \
--upload-file test.jpg \
"https://..."- 1.Reduce clock skew and choose a realistic expiration
- 2.If the URL is generated on a host with drift or given a too-short lifetime, the request may already be invalid by the time the client uses it.
timedatectl statusPrevention
- Sign URLs with the bucket's actual AWS region, not a guessed default region
- Keep the HTTP method and signed headers stable between generation and use
- Use expiration values that match the real upload or download experience
- Monitor system clock drift on servers that mint presigned URLs