This article is written by Anel Kalajevac, Senior Software Engineer and Tech Lead at HTEC Group
In my previous blog post, I wrote about the importance of web application performance, different performance bottlenecks, and how to identify them. Also, I described a solution for heavy javascript usage, the most common performance bottleneck.
Large image sizes are among the most common bottlenecks, especially for customer-facing web applications (such as e-commerce) where images are crucial, as they visually convey product information, establish trust, and engage customers –all of which significantly influence purchase decisions in the absence of physical interaction. I will show you a conceptual solution for the optimization of large images and improving web application performance. But first, let’s see how large image sizes affect web applications.
Large image size – UX & SEO killer
Large image sizes impact web application performance by increasing load times, leading to slower page rendering and potentially frustrating user experiences. Additionally, large image sizes consume more bandwidth, impacting users with limited data plans or slower internet connections. From an SEO perspective, slow-loading pages can result in lower search engine rankings, as search engines consider page speed as a ranking factor. Furthermore, users are more likely to leave a site that loads slowly, increasing bounce rates and decreasing engagement.
In other words, large image sizes are a real nightmare for web application performance.
Idea
The first solution that comes to mind for large image size bottleneck is to reduce the image size, but there are more things to consider:
- Who and when will trigger an image optimization, job, or specific request?
- Where to store optimized images?
- Image quality is important because it directly affects the user experience and overall perception of the web application.
- Device type and screen are important because images optimized for desktop screens will not be a good fit for mobile or tablet device screens.
It would be beneficial to optimize the image during the image request and store it in the shared storage for future requests. The optimization process should include features such as resizing, cropping, and quality definition to guarantee a good user experience and compatibility with various devices and screens.
Solution
The proposed solution will be based on Amazon CloudFront for content delivery, Amazon S3 for storage, and AWS Lambda for image processing. Image optimization will be executed centrally in an AWS region only if the image has not been previously optimized and stored.
The previous diagram explains the image optimization flow with the following steps:
- The user sends a request for an image with a defined query parameter for the image source, width/height, and quality, something like:
https://test.com/optimize-image?src=https://image-host.com/image1.jpg&width=500&quality=80.
- The nearby CloudFront edge location handles the request for optimal performance. Prior to passing the request upstream, a CloudFront Function is executed on the viewer request event to rewrite the request URL. In our solution, we rewrite the URL to validate the requested optimization parameters and normalize the URL by ordering the parameters and converting them to lowercase, thus improving the cache hit ratio.
- If the image requested is already stored in the CloudFront cache, then it will be served directly without any additional processing. However, if the image is not found in the cache, the request will be forwarded to an S3 bucket that is dedicated to storing optimized images. If the requested image is optimized and already available in the S3 bucket, it will be served and cached in CloudFront without any further processing.
- If the requested image is not already optimized and available in the S3 bucket, it will respond with a 403 error code. Following that, CloudFront will retry the same URL, but this time, it will use the secondary origin based on the Lambda function URL. Once the Lambda function is invoked, it will download the original image from the provided URL, optimize it using the Sharp library, and store the optimized image in S3. Finally, CloudFront will serve the optimized image and cache it for future requests.
Cost analysis
The cost of this solution depends on various factors, including the size of the image library and overall configuration. To better understand the cost calculation, let’s make a few assumptions for the eu-central-1 region:
- 1 million monthly requests, where:
– 80% will have Cache Hit Ratio,
– 10% will fetch already optimized image from S3 and cache on CloudFront,
– 10% will trigger optimization, store optimized image variation on S3 and cache on CloudFront. - Average size of original image is 1 MB.
- Average size of optimized image is 25 KB.
S3 Standard:
- 1,000,000 requests x 0.1 x 25 KB / 1024 / 1024 = 2.38 GB of optimized images per month
- If we consider S3 lifecycle policy that deletes optimized images after 90 days, in the worst case we will have: 2.38 GB x 3 = 7.14 GB stored
- Tiered price: 7.14 GB x 0.0245 USD = 0.17 USD
- Total tier cost = 0.1749 USD (S3 Standard storage cost)
- 100,000 PUT requests for S3 Standard Storage x 0.0000054 USD per request = 0.54 USD (S3 Standard PUT requests cost)
- 100,000 GET requests in a month x 0.00000043 USD per request = 0.043 USD (S3 Standard GET requests cost)
- 2.38 GB x 0.0008 USD = 0.0019 USD (S3 select returned cost)
- 2.38 GB x 0.00225 USD = 0.0054 USD (S3 select scanned cost)
- 0.1749 USD + 0.043 USD + 0.54 USD + 0.0019 USD + 0.0054 USD = 0.77 USD (Total S3 Standard Storage, data requests, S3 select cost)
- S3 Standard cost (monthly): 0.77 USD
CloudFront:
- 1,000,000 requests x 25 KB / 1024 / 1024 = 23.84 GB
- Tiered price: 23.84 GB x 0.085 USD = 2.03 USD
- Total tier cost = 2.03 USD (Data transfer out to internet from Europe)
- Data transfer out to internet cost: 2.03 USD
- 1,000,000 requests x 0.0000012 USD = 1.20 USD (HTTPS requests from Europe)
- Requests cost: 1.20 USD
- CloudFront function: 0.1 USD
- 2.03 USD + 1.20 USD + 0.1 USD = 3.24 USD (Total cost Europe)
- CloudFront price Europe (monthly): 3.24 USD
AWS Lambda (2 GB, 100ms average execution duration):
- 1,000,000 requests x 0.1 x 100 ms x 0.001 ms to sec conversion factor = 10,000.00 total compute (seconds)
- 2 GB x 10,000.00 seconds = 20,000.00 total compute (GB-s)
- 20,000.00 GB-s – 400000 free tier GB-s = -380,000.00 GB-s
- Max (-380000.00 GB-s, 0) = 0.00 total billable GB-s
- Tiered price for: 0.00 GB-s
- Total tier cost = 0.00 USD (monthly compute charges)
- 100,000 requests – 1000000 free tier requests = -900,000 monthly billable requests
- Max (-900000 monthly billable requests, 0 ) = 0.00 total monthly billable requests
- 0 GB – 0.5 GB (no additional charge) = 0.00 GB billable ephemeral storage per function
- Lambda costs – With Free Tier (monthly): 0.00 USD
The total cost of the described solution with few assumptions for the eu-central-1 region is 0.77 USD + 3.24 USD = 4.01 USD (monthly).
Summary
In conclusion, optimizing large image sizes is crucial for enhancing web application performance, user experience, and SEO rankings. The proposed solution leveraging Amazon CloudFront, Amazon S3, and AWS Lambda offers an effective way to address this challenge. By considering factors like image optimization, storage, and content delivery, the solution provides a cost-efficient approach to handling large volumes of images. With a monthly estimated cost of 4.01 USD in the EU-central-1 region, this solution provides a balance between performance improvements and economic considerations for web applications reliant on impactful visuals, such as those in the e-commerce domain.