Taking Full Advantage of CloudFront

We (AdMob) are exploring Amazon’s new AWS service CloudFront which is a simple CDN. Amazon built this services as a true CDN that people could use instead of using S3 what it wasn’t meant for.

There’s a few things I’ve discovered about CloudFront that bear repeating here. These points are important if you’re attempting to use CloudFront to serve up static portions of your website.

  • CloudFront pulls from S3 only if the CF node doesn’t already have a local copy. This means that the only way to push out a new file is to change the filename. (style.v1.css, styles.v2.css, etc.) This means that your framework will have to take advantage of this. Without file versioning you’re at risk of serving stale files from different nodes on CloudFront.
  • CloudFront doesn’t automatically detect if a browser accepts gzip encoding. You will have to keep separate versions of gzip files (and upload them pre-gzipped). The easiest way to do this is to keep a separate CNAME, eg static.domain.com and staticu.domain.com (u for uncompressed). Your website will then detect the Accept-Encoding header from the client and serve up the appropriate file. Gzipping could decrease your static content by 75% which not only speeds things up for your users but decrease your CloudFront costs.
  • You should set the appropriate headers when uploading content to S3. That means setting a far futures Expires Header and a Cache-Control header. You can read more about these here. There are two GUI programs that allow you to do this, BucketExplorer and jets3t. If you want to write scripts, I would suggest ruby and the AWS::S3 gem.

Here is some ruby code to do what I’ve described above.

require 'rubygems'
require 'aws/s3'
require 'stringio'
require 'zlib'

  :access_key_id => '[key]', 
  :secret_access_key => '[key]'

strio = StringIO.open('', 'w')
gz = Zlib::GzipWriter.new(strio)
AWS::S3::S3Object.store('[s3 location]', 
                        '[s3 bucket]', 
                        :access => :public_read, 
                        'Content-Encoding' => 'gzip', 
                        'Content-Type' => 'application/x-javascript', 
                        'Expires' => 'Fri, 16 Nov 2018 22:09:29 GMT')