An Open Source and Completely Serverless Image Resizing Service in AWS

Go straight to the Github Repo

The need to properly size and optimize images within web and app content is such a common problem that there are numerous — if not seemingly innumerable — solutions out there. Generally, you’ll find this to be just one feature of Digital Asset Managers (or DAMs), like Cloudinary, Widen, and Bynder. These services also provide many more primary features — proofing, approval workflows, asset organization, content localization, slick UIs, etc. — and you’ll pay handsomely. What if I don’t need all of that, and just want to be able to dynamically (at request time) resize images?

Isn’t there already open source for this?

There are actually quite a few public Github repos out there to address this problem. A couple of years back (in the early days of Lambda@Edge) I was looking for a solution and found this tutorial on the official AWS blog. Of course, that tutorial was just intended as a demonstration of what was capable and not instruction on building a production-ready solution per sé. In fact, both the architecture and the code were a little lacking. Still, I tried to use that blog post as a jump-off to create several subsequent solutions. The problem is that all of the blogs, tutorials, walkthroughs, and open source solutions that I’ve been able to find since then are just as incomplete, as if they’ve all learned from the same example.

TLDR;

Yeah, this is a bit long-winded, so I’ll just copy-paste the Conclusion here.

Introducing: Image Flex

Image Flex is a robust and fully open source image resizing service built on AWS Serverless technologies and used to resize, optimize, and cache images on “the edge,” on the fly. Served by CloudFront via an Origin Access Identity. Executed on Lambda@Edge. Backed by S3. Protected by AWS WAF. Provisioned via CloudFormation. Built and deployed by the Serverless Application Model (SAM) CLI.

The Approach

I started by using what I evangelize as “template-driven architecture.” That is, I don’t manually create or configure any resources. All of the infrastructure will be code, configured via a SAM/CloudFormation template, and only via that template. This is a good (if not best) practice to prevent drift between your actual resources and your template, or vice versa. Other good solutions for template-driven architecture include Terraform, Ansible, and Serverless Framework.

Architecture

Provisioning

The CloudFormation template implements AWS’s Serverless Application Model (SAM) syntax. SAM provides an alternative shorthand for defining certain serverless-specific resources in a CloudFormation template. Note that SAM resource definitions can differ dramatically from the same resource definitions using straight CloudFormation syntax.

Security

The entire service is behind an AWS WAF Web access control list. I only employ the AWS Common Rule Set to defend against numerous vulnerabilities. You could further lock it down by whitelisting requester IP addresses or geo-locking requests.

Storage

To host both the original and optimized images, S3 was the obvious and only choice here. A private S3 bucket resource is defined, blocking all public access except by the CloudFront distribution, which employs the Origin Access ID. The bucket definition also includes CORS settings that allow clients on any domain to GET images from the service. You might want to lock this down further by providing a list of AllowedOrigins (domains from which your images may be requested).

Caching

Now that we have a bucket to host our images, we’ll want to put a CDN in front of it to enable caching. A CDN (content delivery network) like CloudFront will serve cached copies of the images from edge locations closest to the actual user. The definition for the CloudFront distribution specifies the hosting S3 bucket (above) as its origin, as well as the Origin Access ID to use to be allowed access to that bucket. The definition also specifies the WAF ACL to use (created earlier). Lastly and as mentioned in the previous section, the definition also enables saving logs to an S3 bucket. These logs will have an entry for every request to the CloudFront distribution.

Compute

To process our requests (and to prevent having to manually run servers) Lambdas were a perfect fit. These Lambdas scripts are actually Lambda@Edge scripts, as they will be attached to different parts of the CloudFront request/response event cycle, therefore executing on the CloudFront edge servers.

CloudFront Events That Can Trigger a Lambda Function
CloudFront Events That Can Trigger a Lambda Function
  1. If the response from the origin has an HTTP status of 403 or 404, fetch the base image, resize to the requested dimensions, save the resized copy of the image to S3, and return that new image in the response.

Usage

Using the service is as simple as constructing a URL to request an image.

https://my.service.url/myphoto.png
https://my.service.url/myphoto.png?w=800
https://my.service.url/800/myphoto.webp
https://my.service.url/myphoto.png?w=400&h=400
https://my.service.url/400x400/myphoto.webp

Try it out

If all you need is to host, resize, optimize, and cache images, don’t break the bank on a Digital Asset Management solution. Building a robust solution is simple with a little knowledge of a handful of AWS services, and I went ahead and did it all for you. Check out my fully documented and production-ready solution on Github, which builds and deploys in just 2 commands!

Senior Director, Engineering. Principal full-stack. AWS Certified. Scrum Certified. Serverless Ninja. Crafter of Dapps. Minter of NFTs.