How do you get CloudFront to create thumbnails

How do you get CloudFront to create thumbnails

So, you’re using AWS S3 to store your images. That makes sense. It also makes sense to use a Lambda function to create thumbnails of them on the fly. If you’re not doing this yet then you should be. It’s one of the classic use cases for AWS Lambda and there are loads of examples around for it including this one from AWS themselves. But, if you also use Cloud Front to serve the images how do you get it to trigger the image thumbnails? CloudFront copies objects directly from S3 so it won’t actually trigger the Lambda thumbnail function. Luckily you can force the thumbnail to be created by changing the image tag.

One of the first examples of Lambda which you come across is using it to dynamically create image thumbnails for images stored in AWS S3. The scenario goes something like this. You upload an image to a folder in a publicly available S3 bucket. For example:

http://s3-eu-west-1.amazonaws.com/cactuseros/native/test1.jpg

Using one of the many off the shelf lambda samples you can configure your S3 bucket to dynamically create thumbnails of images when they are first requested. Therefore when on your site you put in a link as below Lambda will kick in and dynamically create the thumbnail storing it back in the bucket in the folder 200x200.

http://s3-eu-west-1.amazonaws.com/cactuseros/200x200/test1.jpg

The first time the page with the image is loaded there will be a slight delay while the image rendition is being created.

If you’ve got a lot of images and a geographically diverse set of users you’ll also want to use CloudFront. CloudFront is a Content Distribution Network (CDN) which stores copies of your site assets in locations which are geographically closer to your users. When users load your pages they see the local copy of the file rather than the original. By connecting CloudFront to your S3 bucket you can ensure that users avoid image latency when loading pages. For more details about setting up CloudFront with S3 see https://aws.amazon.com/documentation/cloudfront/. With CloudFront you’ll be able to access your image using an address something like the one below:

http://d2oriw655p9wkt.cloudfront.net/200x200/test1.jpg

However using Cloud Front and Lambda together can lead you to a situation where your thumbnails are never actually generated. This is because CloudFront copies objects directly out of S3 rather than using an http request. If CloudFront gets a request for an image it can’t find it simply returns an error.

To get around this you can use the html onerror feature to ensure that the thumbnail is generated if it doesn’t exist. As well as specifying the img url you also set an ONERROR condition which points directly to the S3 thumbnail. This means that if the thumbnail exists it will be copied into CloudFront and served from there. If the thumbnail does not exist it the browser will fall back to the ONERROR code and make the request directly to the S3 bucket therefore triggering the Lambda function.

Here’s an example from one of my sites:

<img src=“http://d2oriw655p9wkt.cloudfront.net/200x200/test1.jpg” onerror=“this.onerror=null;https://s3-eu-west-1.amazonaws.com/cactuseros/200x200/test1.jpg”/>

Notice the “this.onerror=null” in the code. This stops the browser from going into an endless loop if the onerror image does not exist.

Finally it’s a good idea to use CNAMES and redirects to create friendly names for your image locations. This not only looks better but makes it easier to move your storage locations around. Here I’m using a CNAME on the CloudFront distribution so that the image link becomes

http://images.cactuseros.com/test1.jpg

I’m also using a Reverse Proxy redirect for the S3 image source so that the S3 image url is:

http://www.cactuseros.com/images/test1.jpg

This means that my final img tag becomes something like:

<img src=“http://images.cactuseros.com/200x200/test1.jpg" onerror=“this.onerror=null;http://www.cactuseros.com/images/200x200/test1.jpg”/>

The browser will first call the image from the CloudFront url on the images.cactuseros.com CNAME. If it doesn’t exist it will request the same image from the S3 bucket. This triggers the Lambda function to create the image rendition. The next time someone loads the page the rendition will exist so the image will be served from CloudFront.