AmazonS3 generatePresignedUrl SignatureDoesNotMatch 403

Recently I’m playing with AWS S3 client to generate a URL for client to upload files to our S3 bucket. The code example I referenced can be found here


import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.HttpMethod;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;

public class GeneratePresignedUrlAndUploadObject {
	private static String bucketName = "*** bucket name ***";
	private static String objectKey  = "*** The key to your object ***";

	public static void main(String[] args) throws IOException {
		AmazonS3 s3client = new AmazonS3Client(new ProfileCredentialsProvider());

		try {
			System.out.println("Generating pre-signed URL.");
			java.util.Date expiration = new java.util.Date();
			long milliSeconds = expiration.getTime();
			milliSeconds += 1000 * 60 * 60; // Add 1 hour.

			GeneratePresignedUrlRequest generatePresignedUrlRequest =
				    new GeneratePresignedUrlRequest(bucketName, objectKey);

			URL url = s3client.generatePresignedUrl(generatePresignedUrlRequest); 


			System.out.println("Pre-Signed URL = " + url.toString());
		} catch (AmazonServiceException exception) {
			System.out.println("Caught an AmazonServiceException, " +
					"which means your request made it " +
					"to Amazon S3, but was rejected with an error response " +
			"for some reason.");
			System.out.println("Error Message: " + exception.getMessage());
			System.out.println("HTTP  Code: "    + exception.getStatusCode());
			System.out.println("AWS Error Code:" + exception.getErrorCode());
			System.out.println("Error Type:    " + exception.getErrorType());
			System.out.println("Request ID:    " + exception.getRequestId());
		} catch (AmazonClientException ace) {
			System.out.println("Caught an AmazonClientException, " +
					"which means the client encountered " +
					"an internal error while trying to communicate" +
					" with S3, " +
			"such as not being able to access the network.");
			System.out.println("Error Message: " + ace.getMessage());

	public static void UploadObject(URL url) throws IOException
		HttpURLConnection connection=(HttpURLConnection) url.openConnection();
		OutputStreamWriter out = new OutputStreamWriter(
		out.write("This text uploaded as object.");
		int responseCode = connection.getResponseCode();
		System.out.println("Service returned response code " + responseCode);


The above code works perfectly, I can get signed URL for file upload and upload file using uploadObject method. However, when I used CURL command to upload the file:

$ curl -X PUT –data-binary @/Users/Harvey/Kodak.jpg “”

I always get 403 “SignatureDoesNotMatch” error. What might be wrong? Forgot to set AWS S3 region? Expiry time too short? Signature isn’t URL encoded? After ruling out all those possibility, finally I found some clues from reading this stackoverflow question

The issue was the HTTP Content-Type header!!! In the code example above, there’s no ContentType set, however CURL command sends an implicit ContentType HTTP header (you can find it by adding -v to CURL command)

$ curl -v -X PUT –data-binary @/Users/Harvey/Kodak.jpg “”

The default one sent by CURL is  Content-Type: application/x-www-form-urlencoded

The solution

Once the root cause is found, it’s quite easy to fix it.

1.  Set the correct contentType is the GeneratePresignedUrlRequest before generating the signed URL

generatePresignedUrlRequest.setContentType(contentType); //e.g. image/jpeg
2. Always specify the correct contentType in CURL command (or whatever client code you use)
$ curl --header "Content-Type: image/jpeg" -X PUT --data-binary @/Users/Harvey/Kodak.jpg ""
(Visited 196 times, 3 visits today)

Leave a Reply