I need to set cache-control headers for an entire s3 bucket, both existing and future files and was hoping to do it in a bucket policy. I know I can edit the existing ones and I know how to specify them on put if I upload them myself but unfortunately the app that uploads them cannot set the headers as it uses s3fs to copy the files there.
There are now 3 ways to get this done: via the AWS Console, via the command line, or via the s3cmd command line tool.
AWS Console Instructions
This is now the recommended solution. It is straight forward, but it can take some time.
- Log in to AWS Management Console
- Go into S3 bucket
- Select all files by route
- Choose "More" from the menu
- Select "Change metadata"
- In the "Key" field, select "Cache-Control" from the drop down menu max-age=604800 Enter (7 days) for Value
- Press "Save" button
(thanks to @biplob - please give him some love below)
AWS Command Line Solution
Originally, when I created this bucket policies were a no go, so I figured how to do it using aws-cli, and it is pretty slick. When researching I couldn't find any examples in the wild, so I thought I would post some of my solutions to help those in need.
NOTE: By default, aws-cli only copies a file's current metadata, EVEN IF YOU SPECIFY NEW METADATA.
To use the metadata that is specified on the command line, you need to add the '--metadata-directive REPLACE' flag. Here are a some examples.
For a single file
aws s3 cp s3://mybucket/file.txt s3://mybucket/file.txt --metadata-directive REPLACE \
--expires 2034-01-01T00:00:00Z --acl public-read --cache-control max-age=2592000,public
For an entire bucket (note --recursive flag):
aws s3 cp s3://mybucket/ s3://mybucket/ --recursive --metadata-directive REPLACE \
--expires 2034-01-01T00:00:00Z --acl public-read --cache-control max-age=2592000,public
A little gotcha I found, if you only want to apply it to a specific file type, you need to exclude all the files, then include the ones you want.
Only jpgs and pngs:
aws s3 cp s3://mybucket/ s3://mybucket/ --exclude "*" --include "*.jpg" --include "*.png" \
--recursive --metadata-directive REPLACE --expires 2034-01-01T00:00:00Z --acl public-read \
--cache-control max-age=2592000,public
Here are some links to the manual if you need more info:
- http://docs.aws.amazon.com/cli/latest/userguide/using-s3-commands.html
- http://docs.aws.amazon.com/cli/latest/reference/s3/cp.html#options
Known Issues:
"Unknown options: --metadata-directive, REPLACE"
this can be caused by an out of date awscli - see @eliotRosewater's answer below
S3cmd tool
S3cmd is a "Command line tool for managing Amazon S3 and CloudFront services". While this solution requires a git pull it might be a simpler and more comprehensive solution.
For full instructions, see @ashishyadaveee11's post below
cp
download and re-upload everything? –
Herzberg --cache-control
without REPLACE
, but can you use --metadata-directive COPY --metadata {"cache-control": "max-age=31536000"}
to add rather than replace? –
Adrianadriana aws s3 cp s3://BucketName/index.html s3://BucketName/index.html --metadata-directive REPLACE --cache-control max-age=0,no-store,must-revalidate
–
Paternal expires
? developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expires If there is a Cache-Control header with the max-age or s-maxage directive in the response, the Expires header is ignored. –
Vulcanism --metadata-directive
entirely and only use --cache-control max-age=31536000
it's a valid option and this way you preserve the content-type... –
Vulcanism Now, it can be changed easily from the AWS console.
- Log in to AWS Management Console
- Go into S3 bucket
- Select all files by route
- Choose "More" from the menu
- Select "Change metadata"
- In the "Key" field, select "Cache-Control" from the drop down menu
- max-age=604800 Enter (7 days) for Value
- Press "Save" button
It takes time to execute depends on your bucket files. Redo from the beginning if you accidentally close the browser.
steps
git clone https://github.com/s3tools/s3cmd
- Run
s3cmd --configure
(You will be asked for the two keys - copy and paste them from your confirmation email or from your Amazon account page. Be careful when copying them! They are case sensitive and must be entered accurately or you'll keep getting errors about invalid signatures or similar. Remember to adds3:ListAllMyBuckets
permissions to the keys or you will get anAccessDenied
error while testing access.) ./s3cmd --recursive modify --add-header="Cache-Control:public ,max-age= 31536000" s3://your_bucket_name/
I had been banging my head on this problem for a while now. Until I found & read the docs. Sharing that here in case it helps anyone else:
- Amazon CloudFront Documentation: Specifying How Long Objects Stay in a CloudFront Edge Cache (Expiration)
What ended up reliably working for me was this command. I chose a 1 second expiration time for testing to verify expected results:
aws s3 cp \
--metadata-directive REPLACE \
--cache-control max-age=1,s-maxage=1 \
s3://bucket/path/file \
s3://bucket/path/file
--metadata-directive REPLACE
is required when "cp
" modifying metadata on an existing file in S3max-age
sets Browser caching age, in secondss-maxage
sets CloudFront caching, in seconds
Likewise, if setting these Cache-Control
header values on a file while uploading to S3, the command would look like:
aws s3 cp \
--cache-control max-age=1,s-maxage=1 \
/local/path/file \
s3://bucket/path/file
I don't think you can specify this at the bucket level but there are a few workarounds for you.
Copy the object to itself on S3 setting the appropriate
cache-control
headers for the copy operation.Specify response headers in the url to the files. You need to use pre-signed urls for this to work but you can specify certain response headers in the querystring including
cache-control
andexpires
. For a full list of the available options see: http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectGET.html?r=5225
You can always configure a lambda with a trigger on PUTOBJECT on S3, the lambda will simply change the header of this particular object that was just put.
Then you can run the copy command mentioned above one last time, and all the new objects will be fixed by the lambda.
UPDATE:
Here is a good place to start from: https://www.aaronfagan.ca/blog/2017/how-to-configure-aws-lambda-to-automatically-set-cache-control-headers-on-s3-objects/
To those attempting to use Dan's answer and getting the error:
"Unknown options: --metadata-directive, REPLACE"
I ran into the issue, and the problem was that I installed awscli using
sudo apt-get install awscli
This installed an old version of the awscli which is missing the --metadata-directive command. So I used sudo apt-get remove awscli to remove it.
Then reinstalled following the procedure from amazon: http://docs.aws.amazon.com/streams/latest/dev/kinesis-tutorial-cli-installation.html
The only difference is that I had to use sudo -H because of permission issues which others might run into also.
Bucket policies are to give permissions to the bucket and the object stored inside, so this road won't yield the results you are looking for. The other answers modify the object metadata using automated means, but you can also use Lambda@Edge if you are willing to move the bucket behind CloudFront.
With Lambda@Edge you can run arbitrary code for each client request and it can change the headers returned from the origin (S3 bucket in this case). It requires a bit more configuration and it costs some money, but here's a blueprint of the solution:
- create a CloudFront distribution
- add the S3 bucket as the origin
- create a lambda function that modifies the response header
- use the CloudFront distribution's URL to access the files
The AWS documentation has an example how to modify response headers. If you happen to use Terraform to manage the infrastructure I've written an article how to do it.
Previous answers either don't really correspond with the question or incur a cost (Lambda).
What you should do is to set "cache-control" header when you upload the file (PutObject or MultiPartUpload).
Depending on your language, it can be somewhat different. The documentation is not very clear (as presumably AWS hopes you would pay them with the other solutions).
An example with PHP:
$uploader = new MultipartUploader ($s3,$filename,[
...,
'before_initiate' => function(\Aws\Command $command){
$command['CacheControl'] = 'max-age=31536000,public';
},
...
]);
Another example with Go:
cc := "max-age=31536000,public"
input := &s3.PutObjectInput{
...,
CacheControl: &cc,
}
Figured I'd share my usage since previous answers misled me. Only two commands with AWS CLI:
aws s3 cp s3://bucketname/ s3://bucketname/ --cache-control max-age=12345 --recursive
That's it for already existing stuff, using cp. Setting --cache-control
like that is a valid option.
If you are uploading you might as well sync, for which the command is:
aws s3 sync z:\source\folder s3://bucketname/folder --delete --cache-control max-age=12345 --acl public-read
Notice that I do not use --metadata-directive
AT ALL, since by using it you'll lose your guessed content types which will make stuff like images not display by a browser but get downloaded instantly. My solution preserves the guessed value, and allows the guessing with the sync.
Adding to @roens answer.
If you use S3 with Cloudfront, you can easily use not just Cloudfront, but also browser-caching. For that to work, just specify the cache-control header in the response.
- Go to Cloudfront
- Go to your distribution and edit behaviour
- Go to response headers policy
- Define a custom policy with cache-control "max-age=604800" (7 days) and orgin-override enabled.
- Now you should have a 7 day browser cache on all your files within this S3 bucket.
Remark: In S3 you would have to specify this for every file, which might no be suitable for your application. Also you would have to define a reponse header policy to pass the cache-control header, if you use S3 together with Cloudfront.
© 2022 - 2024 — McMap. All rights reserved.
cache-control: max-age
on a CloudFront distribution: see this answer on another thread; another solution, for anyone pushing assets via a deploy script withaws s3
: set a value via--cache-control max-age=
. – Fancher