Wednesday, June 28, 2017

How to define the Cache-Control header on an S3 object via a signed URL?

Leave a Comment

Following the instructions in this guide, I've managed to get uploads working via signed URLs. It looks something like this:

const s3 = new aws.S3();  const s3Params = {   Bucket: S3_BUCKET,   Key: fileName,   Expires: 60,   ContentType: fileType,   ACL: 'public-read',   CacheControl: 'public, max-age=31536000', };  s3.getSignedUrl('putObject', s3Params, (err, data) => {   // ... }); 

...except my CacheControl param (which I added myself; it isn't in the guide) does not seem to take effect. When I use the above code to generate a signed URL and upload something to it, the resulting object in S3 is served with no Cache-Control header.

What am I doing wrong?

1 Answers

Answers 1

You must send the Cache-Control header in the upload request, regardless of what you set during the signed URL generation.

Whether this is a bug or an intentional behaviour is questionable and beyond my ability to answer. The Cache-Control header, as you noticed, is part of the signed URL, but for whatever reason the information is completely ignored during the file upload, ie. not specifying a CacheControl property in the getSignedUrl() function still allows the client to set Cache-Control header to whatever value they choose.

If you need to have control over the Cache-Control header, then using the getSignedUrl() is most likely not appropriate for your use case.

Solution

AWS now supports a new signature scheme, called AWS Signature version 4 which allows full control over what the upload request may or may not contain, including which headers are sent and with what values.

The JavaScript SDK supports this new signature version: createPresignedPost().

A detailed example of how to generate this pre-signed POST policy and how the upload form should look like can be found directly on AWS's documentation.

Even though the example demonstrates the file upload via standard http upload <form> element, the principles can be applied to any client/consumer capable of performing HTTP communication.

Example

For completeness, here is the example (taken from AWS documentation page linked above) of how a pre-signed POST policy looks like:

{ "expiration": "2015-12-30T12:00:00.000Z",   "conditions": [     {"bucket": "sigv4examplebucket"},     ["starts-with", "$key", "user/user1/"],     {"acl": "public-read"},     {"success_action_redirect": "http://sigv4examplebucket.s3.amazonaws.com/successful_upload.html"},     ["starts-with", "$Content-Type", "image/"],     {"x-amz-meta-uuid": "14365123651274"},     {"x-amz-server-side-encryption": "AES256"},     ["starts-with", "$x-amz-meta-tag", ""],      {"x-amz-credential": "AKIAIOSFODNN7EXAMPLE/20151229/us-east-1/s3/aws4_request"},     {"x-amz-algorithm": "AWS4-HMAC-SHA256"},     {"x-amz-date": "20151229T000000Z" }   ] } 

This POST policy sets the following conditions on the request:

  • The upload must occur before midnight UTC on December 30, 2015.
  • The content can be uploaded only to the sigv4examplebucket. The bucket must be in the region that you specified in the credential scope (x-amz-credential form parameter), because the signature you provided is valid only within this scope.
  • You can provide any key name that starts with user/user1. For example, user/user1/MyPhoto.jpg.
  • The ACL must be set to public-read.
  • If the upload succeeds, the user's browser is redirected to http://sigv4examplebucket.s3.amazonaws.com/successful_upload.html.
  • The object must be an image file.
  • The x-amz-meta-uuid tag must be set to 14365123651274.
  • The x-amz-meta-tag can contain any value.

Note that the list of conditions in this example is not exhaustive and CacheControl is supported. See the creating a POST policy document for what you can do with this.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment