
12 Mar 2020 Dynamically resize your images using CloudFront OriginGroup
Just want the code? Go here!
It’s Saturday morning and you need to take your dog (or a cat. Or a baby.) for a walk. You bring your phone which has an overcomplicated camera with you. You decided to use it.

Boom! You just became a photographer. Now, this photograph is worth sharing on your website. The problem is, the image is 6MB in size and you’re too lazy to resize it before uploading it to your website.
This blog will walk you through how to dynamically resize your image so that you don’t have to do it yourself.
Architecture Introduction
Just so we’re clear. This blog only covers the dynamic resizing image part. You have to create your website yourselves.

From the diagram above, the only notable thing (aside from the standard CloudFront configs), is that there are 2 origins.
- S3 “CDN” Origin: this is where your resized files live after they were born.
- API Gateway Origin: this is where the resizing happens.
Note that we have 2 buckets. The first one is a “Private” bucket and the second one is a “CDN” bucket. The S3 Origin
I mentioned above is using the “CDN” bucket.
The “Private” bucket is a place where you can upload your stuff. And the “CDN” bucket is a place where your users can see your stuff.
These “Private” and “CDN” buckets are like totally opinionated and optional. You can have only 1 bucket to do this.
So the flow is something like this:
- You upload your gorgeous picture
- Your picture stored in your “Private” bucket
- Someone wants to see your gorgeous picture
- CloudFront can’t find that picture in the “CDN” bucket
- CloudFront tries the second origin (Resizing function)
- Resizing function grabs that image from your “Private” bucket and resizes the image to the requested size. Then, it puts that resized image to the “CDN” bucket for subsequent access.
- CloudFront returns the image from your resizing function.
The code
The first step is to install serverless
in an empty NPM project. We’ll orchestrate our infrastructure using that framework.
npm install -D serverless
Now create a serverless.yml
file in your root project. Please refer to this file for a complete reference.
That template will produce these resources:
- “Private” bucket: a place where you upload your original file
- “CDN” bucket: a place where the resized images live for subsequent accesses
- Bucket Policy for “CDN” bucket: used to only allow our CloudFront distribution to access our files
- Origin Access Identity: an identity for our CloudFront distribution. related to #3
- CloudFront Distribution: our CDN
- API Gateway + Lambda + IAM Role: our resizing function
I’m going to talk about the CloudFront distribution (#5) and the resizing function (#6) as the other resources do not have any special stuffs related to the topic.
CloudFront Distribution
The configuration that makes this thing functional lies in the OriginGroups
section. Essentially, the CDN will have a secondary origin in case the item does not exist in the primary one. In our case, our primary origin is the “CDN” bucket (S3Origin
) and the secondary origin is our resizing function (APIGatewayOrigin
).
Pay attention to the order of those origins. The first one will be the primary origin.
…and regarding the
Quantity
attribute, I don’t know why that’s required. But it’s marked asRequired
in the documentation.
OriginGroups:
Items:
- Id: DynamicImageGroup
Members:
Items:
- OriginId: S3Origin
- OriginId: APIGatewayOrigin
Quantity: 2
FailoverCriteria:
StatusCodes:
Items:
- 403 # S3 will throw 403 when the image doesn't exist
Quantity: 1
Quantity: 1
Origins:
- Id: S3Origin
DomainName:
Fn::GetAtt:
- CDNBucket
- RegionalDomainName
S3OriginConfig:
OriginAccessIdentity:
Fn::Join:
- /
- - origin-access-identity
- cloudfront
- !Ref CDNOriginAccessIdentity
- Id: APIGatewayOrigin
DomainName:
Fn::Join:
[
".",
[
!Ref HttpApi,
execute-api,
!Ref AWS::Region,
amazonaws.com,
],
]
CustomOriginConfig:
HTTPSPort: 443
OriginProtocolPolicy: https-only
And also, use the OriginGroup’s Id in your DefaultCacheBehavior
.
DefaultCacheBehavior:
# other attributes...
TargetOriginId: DynamicImageGroup
API Gateway + Lambda
This section of the template will produce an HTTP endpoint for your resizing function.
functions:
cdn-image:
runtime: nodejs12.x
handler: src/index.handler
memorySize: 3008
events:
- httpApi:
path: /image/{width}/{image+}
method: get
environment:
CDN_BUCKET_NAME: ${self:custom.userParams.CDN_BUCKET}
PRIVATE_BUCKET_NAME: ${self:custom.userParams.PRIVATE_BUCKET}
With our HTTP configuration, you need to access the image by using an /image/{width}
prefix. So, if you save your image in s3://your-private-bucket/puppy.jpg
, you can access it on https://cloudfrontdomain.com/image/720/puppy.jpg
You need to do the actual resizing in the lambda function. You can do anything you like in the resizing function. Just make sure to return an image (and make sure it’s the image that the user expects).
The sample code that I made below will do these things:
- Get your image from the “Private” bucket
- Resize that image and put it to the”CDN” bucket (with
sharp
library) - Return the image (in base64 encoded binary)
With sharp
library, you need to do some sort of black magic before deploying (if you’re using Mac or Windows)
rm -rf node_modules/sharp && npm install --arch=x64 --platform=linux --target=12.13.1 sharp
This black magic is associated with a documentation if you want further readings.
Now you have to wait like 15 – 30 minutes before the stack is ready.
When you access it (e.g. https://d3h26nxsv4jv3l.cloudfront.net/image/320/two-yellow-labrador-retriever-puppies-1108099.jpg), you’ll get a resized image!

Not working? I probably missed a thing or two 🙁 You can compare it to this repo. I tried to deploy several times to ensure I’m not misleading you guys.
Thanks for reading 🙂
and a shout out to Gareth Jones who makes this blog much better than it was initially.
david
Posted at 11:08h, 25 Julyhi sir, i have just create a new cloudfront which the domain is d2b5lx2w4u2.cloudfront.net, so CDN_BUCKET=d2b5lx2w4u2.cloudfront.net right?