As a web architect, one of the many issues is asset management. And the most significant issue in assets is images. A naive approach would be to set an image and let the browser resize the image via CSS:
img{height:100%;width:100%;object-fit:contain;}
However, it means that you download the original image. It entails two problems: the size of the original image and the suboptimal browser-based resizing.
This post will cover two alternatives: traditional and brand-new solutions.
Ahead-of-time resizing
The traditional solution to a single image source has been ahead-of-time resizing. Before releasing, designers would take time to provide multiple image versions in different resolutions. On this blog, I’m using this technique. I provide three resolutions to display the post’s main image in different contexts as background images:
large for the post on its page
medium for the post on the home page
small for related posts on a post page
I also remove JPEG metadata for an even higher size reduction.
Yet, the traditional approach is to take advantage of the HTML picture tag:
The HTML element contains zero or more elements and one element to offer alternative versions of an image for different display/device scenarios.
The browser will consider each child element and choose the best match among them. If no matches are found—or the browser doesn’t support the element—the URL of the element’s src attribute is selected. The selected image is then presented in the space occupied by the element.
This way has worked for ages, but it has two issues. First, providing multiple resolutions for each image takes a long time. One could automate the process and get good results with AI.
However, the volume of necessary storage could be twice or thrice the size of the original image, depending on the number of extra resolutions created. In an assets-rich environment, e.g., e-commerce would significantly increase costs.
On-the-fly resizing
I recently stumbled upon imgproxy, a component to resize images on-the-fly:
imgproxy makes websites and apps blazing fast while saving storage and SaaS costs
It offers an endpoint where you can send an encoded URL that defines:
The image to resize and its location, _e.g., local, an HTTP URL, a S3 bucket, etc.
Different sizing parameters, e.g., the dimensions, whether to fit or to fill, etc.
The format. imgproxy supports standard formats such as JPEG and PNG but also more modern ones like WebP and AVIF. It can also choose the best format depending on the ‘ Accept ‘ header.
Many (many!) other options, like watermarking, filtering, rotation, etc.
imgproxy offers both an Open Source free version and a paid version; everything included in this post is part of the former.
One solution would be for the web developer to code each imgproxy URL in the HTML:
It leaks topology-related details on the web page. It’s not a maintainable solution. We can solve the issue with a reverse proxy or an API Gateway. I’ll use Apache APISIX for obvious reasons.
With this approach, the above HTML becomes much more straightforward:
Apache APISIX intercepts requests starting with /resize, rewrites the URL for imgproxy, and forwards the rewritten URL to imgproxy. Here’s the overall flow:
The corresponding Apache APISIX configuration looks like the following:
Catches the width and the image in the regular expression
Format the URL for imgproxy. http://server:3000 is the server hosting the original image; @webp indicates a preference for WebP format (if the browser supports it)
With the above, /resize/200/ai-generated.jpg to Apache APISIX is rewritten as /rs:fill/w:200/plain/http://server:3000/ai-generated.jpg@webp to imgproxy.
Testing
We can set up a small testing sample with Docker Compose:
Simple web server hosting the HTML and the main image
We can now test the above setup with the browser’s Developer Tools, emulating small screen devices, i.e., iPhone SE. The result is the following:
Because of the screen resolution, the image requested is the 400px width, not the original one. You can see it in the request’s URL
The returned image is in WebP format; its weight is 14.4kb
The original JPEG image weighs 154kb, more than ten times as much. It’s a lot of network bandwidth saving!
Discussion
Cutting your storage costs by ten is naturally a great benefit. However, it’s not all unicorns and rainbows. Computing the resized image is a compute-intensive operation; it costs CPU time for each request. Moreover, however efficient imgproxy is, it takes time to create the image. We traded storage costs for CPU costs and now incur a slight performance hit.
To fix it, we need a caching layer in front, either a custom one or, more likely, a CDN . You may object that we will store assets again; thus, storage costs will rise again. However, a considerable difference is that the cache works only for used images, whereas we previously paid for storing all images in the first solution. You can also apply known recipes for caching, such as pre-warming, when you know a group of images will be in high demand, e.g., before an event.
Conclusion
In this post, we described how to use Apache APISIX with imgproxy to reduce the storage cost of images in multiple resolutions. With caching on top, it adds more components to the overall architecture but shrinks down storage costs.
This post was inspired by Andreas Lehr’s talk at StackConf.
The complete source code for this post can be found on GitHub.
We use cookies on our website to give you the most relevant experience by remembering your preferences and repeat visits. By clicking “Accept All”, you consent to the use of ALL the cookies. However, you may visit "Cookie Settings" to provide a controlled consent.
This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience.
Necessary cookies are absolutely essential for the website to function properly. These cookies ensure basic functionalities and security features of the website, anonymously.
Cookie
Duration
Description
cookielawinfo-checkbox-analytics
11 months
This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Analytics".
cookielawinfo-checkbox-functional
11 months
The cookie is set by GDPR cookie consent to record the user consent for the cookies in the category "Functional".
cookielawinfo-checkbox-necessary
11 months
This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Necessary".
cookielawinfo-checkbox-others
11 months
This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Other.
cookielawinfo-checkbox-performance
11 months
This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Performance".
viewed_cookie_policy
11 months
The cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data.
Functional cookies help to perform certain functionalities like sharing the content of the website on social media platforms, collect feedbacks, and other third-party features.
Performance cookies are used to understand and analyze the key performance indexes of the website which helps in delivering a better user experience for the visitors.
Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics the number of visitors, bounce rate, traffic source, etc.
Advertisement cookies are used to provide visitors with relevant ads and marketing campaigns. These cookies track visitors across websites and collect information to provide customized ads.