Skip to content

How do I troubleshoot "SignatureDoesNotMatch" and "403 Forbidden" errors for Amazon S3 presigned URL requests?

4 minute read
1

I used an Amazon Simple Storage Service (Amazon S3) presigned URL to make requests to my Amazon S3 bucket, and I received a "SignatureDoesNotMatch" error message with an "HTTP 403 Forbidden" response code.

Short description

When you generate a presigned URL, the client calculates a unique signature to authenticate the request. Then, Amazon S3 calculates a signature based on the parameters that the client sends in the HTTP request and compares both signatures. If the signatures don't match, then you receive a "SignatureDoesNotMatch" error with an "HTTP 403 Forbidden" response code.

Resolution

Note: If you receive errors when you run AWS Command Line Interface (AWS CLI) commands, then see Troubleshooting errors for the AWS CLI. Also, make sure that you're using the most recent AWS CLI version.

Validate the HTTP action

If you generate a presigned URL, then you assign an HTTP action to the URL. Make sure that the action that the client sends in the HTTP request matches the HTTP action in the URL. For example, if you assign the GET action to the URL, then the action in the request must have the GET HTTP action.

Check that you used the correct secret access key to generate the presigned URL

Check that the code that generated the presigned URL has the correct secret access key. Make sure that there isn't any extra spaces or wrong characters.

If you use the AWS CLI to generate your presigned URL, then configure the credentials with the correct secret access key. Run the cat command to get the current credentials:

cat ~/.aws/credentials

If it's a temporary session, then check that the AWS_SESSION_TOKEN is correct. Verify that no extra spaces or wrong characters passed in the secret access key from when you generated your presigned URL.

Verify that the secret access key in your credentials matches your original secret access key.

Verify that the bucket name and object name match the values in the URL

Verify that the bucket name and object name are correct and match the names that are in the signature generation of the URL.

Verify that the headers correctly generated in the HTTP request

Make sure that the HTTP headers that generate the signature match the headers that the client sends to Amazon S3 in the HTTP request. You must include in your request any headers that you use to sign the URL. The presigned URL contains only the host, path, and query parameters.

If you added a ContentType parameter when you generated a presigned URL, then pass a Content-type header when you send requests for the presigned URL. For example, run the following curl command:

curl -X PUT -T "abcd-video.mp4" -H "Content-Type: video/mp4" "Your-presigned-url"

Note: Replace Your-presigned-url with your presigned URL.

Then, make sure that the value of the header matches the value that you generated when you calculated the signature.

Confirm that the AWS Region is correct

Before you send the presigned URL to Amazon S3, confirm that your Region for the URL matches the current Region of the bucket.

Run the GetBucketLocation API command to check the Region of an S3 bucket. Or, run the get-bucket-location AWS CLI command to verify the Region:

aws s3api get-bucket-location --bucket example-bucket

Note: Replace example-bucket with your bucket name.

Example output:

{      
"LocationConstraint": "us-west-2"  
}

Check that the system time is correctly set in a valid period

If you set the system time to a future date after the digital signature's validity period, then the signature appears as expired and fails. If you set the system time to a past date before the signature's validity period, then the signature isn't valid and fails.

Related information

Why does the presigned URL for my Amazon S3 bucket expire before the expiration time that I specified?

Download and upload objects with presigned URLs

How do I troubleshoot the error "Request has expired" when I try to access an S3 object?

AWS OFFICIALUpdated 6 months ago
3 Comments

This article is frustratingly vague. For example it suggests to "check the secret access key" but gives zero information on how to do that.

replied a year ago

The vagueness of "Make sure that the HTTP headers that you use to generate the signature match the headers that the client sends to S3 in the HTTP request"... okay there are about 16 headers in each request, which ones SPECIFICALLY are you validating? I've tried 439 different combinations and I REPEATEDLY get the SignatureDoesNotMatch error. Oh yeah, and the boto client throws an error when I add the ContentType param to the generate_presigned_url request.

replied a year ago

I had the same issue, because i'm adding content-type while generating a pre-signed-url:-

upload_url = s3_client.generate_presigned_url(
        'put_object',
        Params={
            'Bucket': BUCKET_NAME,
            'Key': key,
            'ContentType': 'video/mp4'  # Adjust based on your needs
        },
        ExpiresIn=3600)

but while using the pre-signed-url in postman, it doesn't send the content-type sometimes like below:-

PUT https://vids-mumbai.s3.amazonaws.com/video-songs/hukum?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AK5678HS4567jhuuiY5UBGY2NY%2F20250607%2Fap-west-1%2Fs3%2Faws4_request&X-Amz-Date=20250607T063320Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=content-type%3Bhost&X-Amz-Signature=65789hjkkj,mhnhgbk,mdfdfvv
Request Headers
User-Agent: PostmanRuntime/7.44.0
Accept: */*
Cache-Control: no-cache
Postman-Token: 4522edd7-53e9-42cd-bf13-288e6ed3b1ff
Host: vids-mumbai.s3.amazonaws.com
Accept-Encoding: gzip, deflate, br
Connection: keep-alive

but when i manually added the content-type to the headers it worked fine for me

replied 10 months ago