Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add content-type for specific file types in Amazon CloudFront?

I have an S3-bucket, that I'm serving using CloudFront. And I want to serve some JSON-files from it.

Out of the box, the CF response doesn't contain any Content-Type headers at all for this file type. The file is just downloaded by the browser as any regular file. However, I want it to have a proper mime type header: Content-Type: application/json.

I know, I can set custom header for any single file manually in S3, however, is it possible to specify some rule for specific filename extensions to add specific HTTP headers to the response in Amazon CloudFront?

like image 559
Slava Fomin II Avatar asked May 18 '16 18:05

Slava Fomin II


2 Answers

If you have no control over how objects are being uploaded to your S3 bucket, you could use a Lambda@Edge function to override the response headers as follows:

(The downside to this method is that it increases latency and incurs additional costs)

  1. Create an IAM policy that will be attached to your lambda function's role in the next step:

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "StatementForCloudWatchLogs",
                "Effect": "Allow",
                "Action": [
                    "logs:CreateLogGroup",
                    "logs:CreateLogStream",
                    "logs:PutLogEvents"
                ],
                "Resource": "*"
            },
            {
                "Sid": "StatementForLambdaFunction",
                "Effect": "Allow",
                "Action": [
                    "lambda:EnableReplication",
                    "lambda:GetFunction"
                ],
                "Resource": [
                    "arn:aws:lambda:us-east-1:{YOUR_ACCOUNT_ID}:function:{FUNCTION_NAME}:{FUNCTION_VERSION}"
                ]
            },
            {
                "Sid": "StatementForIAMServiceLinkedRoles",
                "Effect": "Allow",
                "Action": [
                    "iam:CreateServiceLinkedRole"
                ],
                "Resource": "arn:aws:iam::{YOUR_ACCOUNT_ID}:role/*"
            },
            {
                "Sid": "StatementForCloudFrontDistributions",
                "Effect": "Allow",
                "Action": [
                    "cloudfront:CreateDistribution",
                    "cloudfront:UpdateDistribution"
                ],
                "Resource": "*"
            }
        ]
    }
    
  2. Create a new AWS service IAM role for lambda and attach the policy you created in step 1 to it. Under Trust relationships, make sure you have both; lambda.amazonaws.com and edgelambda.amazonaws.com as trusted entities.

  3. Create your lambda function and then publish a new version (Under Actions). You must create functions with the nodejs6.10 runtime property:

    'use strict';
    
    exports.handler = (event, context, callback) => {
       const response = event.Records[0].cf.response;
    
       if (response.status === '200') {
           response.headers['content-type'] = [{
               'value': 'application/json', // <-- Your desired content type.
               'key': 'Content-Type'
           }];
       }
    
       callback(null, response);
    };
    
  4. Create a new behavior for json files in your CloudFront distribution (for example, path/to/your/json/*.json) and make sure it is placed above the Default (*) behavior.

  5. Under Lambda Function Associations for the newly-created behavior, select the Origin Response event type and enter the ARN for your lambda function that was created in step 3. For example: arn:aws:lambda:us-east-1:{YOUR_ACCOUNT_ID}:function:{FUNCTION_NAME}:{FUNCTION_VERSION}. (NOTE: Your lambda function must be created in the US East (N. Virginia) region and the function ARN must have a numbered version and not $LATEST or an alias).
  6. Create a CloudFront invalidation for your path/to/your/json/* files.

Make sure you read the Requirements and Restrictions on Lambda Functions.

See also Lambda Event Structure for additional info.

like image 117
Khalid T. Avatar answered Oct 05 '22 22:10

Khalid T.


Update: CloudFront itself does not provide a built-in mechanism for manipulating headers, but Lambda@Edge, used in conjunction with CloudFront, provides a mechanism to create hooks that can examine and modify origin response headers. It can't actually examine the response body, but can inject static or heuristically-derived headers. This is potentially a viable workaround if the content is from a known/trusted source and the content-type is known, but should probably not be used for user-submitted content, since an incorrect content-type could cause a browser to misinterpret the payload, and might be a potential exploit vector. Setting the content-type on the object, correctly, when it is uploaded is probably still the better solution.

The original answer here pre-dates Lambda@Edge and refers to native capabilities of CloudFront itself.


CloudFront uses the response headers provided by the origin server, whether it's S3 or a custom origin. CloudFront does not provide a mechanism to rewrite them or to add them.

The solution is to set the Content-Type on the object when originally uploading it into S3.

If you upload the file to S3 with Content-Type set, the same value will be returned when the object is downloaded (whether directly from S3 or through CloudFront). Otherwise, you have to modify objects after upload if you don't want the default Content-Type: binary/octet-stream header that S3 assigns when you don't specify one.

like image 38
Michael - sqlbot Avatar answered Oct 05 '22 23:10

Michael - sqlbot