5

I'm deploying a React App using Cloudfront and S3 and I would like to enable brotli for compression. I've seen some tutorials that address this problem using lambda@edge. The behavior configuration for my Cloudfront distribution is the following

enter image description here

I also have the 'Compress Objects Automatically' option set to 'No'. I have associated the two following lambda functions for origin requests and viewer requests, respectively:

  • origin request:

'use strict';

/**
 * Funciton registered on 'Origin Request' CloudFront Event
 */
exports.handler = (event, context, callback) => {
  const request = event.Records[0].cf.request;
  console.log(JSON.stringify(request));
  const headers = request.headers;
  const isBr = headers['x-compression'] && headers['x-compression'][0].value === "br";
  const isGzip =  headers['x-compression'] && headers['x-compression'][0].value === "gzip";

  /**
  * Update request path based on custom header
  */  
  let extension = "";
  if(isBr)
    extension = ".br";
  else if(isGzip)
    extension = ".gzip";
  request.uri =  request.uri + extension;
  callback(null, request);
};
  • viewer request:

'use strict';

/**
 * Funciton registered on 'Viewer Request' CloudFront Event
 */
exports.handler = (event, context, callback) => {
  const request = event.Records[0].cf.request;
  const headers = request.headers;
  
  console.log(JSON.stringify(request));

  /**
   * Detect if brotli is supported by the browser.
   * Pass a custom header x-compression to be captured by the lambda in the origin-request phase.
   */
  if(headers['accept-encoding'] && headers['accept-encoding'][0].value.indexOf('br') > -1) {
    headers['x-compression'] = [{
      key: 'X-Compression',
      value: 'br'
    }];
  }
  else if(headers['accept-encoding'] && headers['accept-encoding'][0].value.indexOf('gzip') > -1){
    headers['x-compression'] = [{
      key: 'X-Compression',
      value: 'gzip'
    }];
  }
  callback(null, request);
};

When I tried to open the app using Cloudfront's domain name in the browser in throws an Uncaught SyntaxError: illegal character in bundle.js:1. However, when I use S3's endpoint for opening the app it works perfectly. The following is my webpack.config.js:

const path = require('path');
const autoprefixer = require('autoprefixer');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');

module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js',
        chunkFilename: '[id].js',
        publicPath: ''
    },
    resolve: {
        extensions: ['.js', '.jsx']
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                loader: 'babel-loader',
                exclude: /node_modules/
            },
            {
                test: /\.css$/,
                exclude: /node_modules/,
                use: [
                    { loader: 'style-loader' },
                    { 
                        loader: 'css-loader',
                        options: {
                            modules: {
                                localIdentName: "[name]__[local]___[hash:base64:5]",
                            },                                                      
                            sourceMap: true
                        }
                     },
                     { 
                         loader: 'postcss-loader',
                         options: {
                             ident: 'postcss',
                             plugins: () => [
                                 autoprefixer({})
                             ]
                         }
                      }
                ]
            },
            {
                test: /\.(png|jpe?g|gif)$/,
                loader: 'url-loader?limit=10000&name=img/[name].[ext]'
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: __dirname + '/src/index.html',
            filename: 'index.html',
            inject: 'body'
        }),
        new CompressionPlugin({
            filename: '[path].gz',
            threshold: 0,
            minRatio: 2,
            test: /\.(js|css)$/i
          }),
          new CompressionPlugin({
            filename: '[path].br',
            algorithm: 'brotliCompress',
            threshold: 0,
            minRatio: 2,
            test: /\.(js|css)$/i,
            compressionOptions: {level: 11}
          })
    ]
};

I've placed my code in this GitHub repo (I've also uploaded the dist/ directory which is the one I upload to S3 for the deployment). I'm new to this technologies so I ran out of ideas. I also tried adding .html and .png extensions to the plugins constructors in the Webpack configuration file but that didn't work either. Any help would be appreciated, thanks in advance

1 Answers1

4

CloudFront now supports Brotli edge compression. You can set Compress Objects Automatically to 'Yes' and eliminate the Lambda@Edge function. CloudFront will take your uncompressed files from S3 and can compress cacheable requests to Brotli or Gzip at the edge for you.

See here for specific configuration docs: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/ServingCompressedFiles.html

Cristian
  • 1,051
  • 7
  • 10