How to make your images load quickly, securely, and SEO-friendly
Requirements
-
Image uploaded to Photo Center
-
A Thumbor resizer URL (will include arcpublishing.com and resizer)
-
A Thumbor resizer key (protects your resizer URL from abuse)
Images play a large role in whether your site feels fast or not. While you want to serve the smallest images only when the images are visible, there are other concerns like high-resolution screens that require bigger images. To handle this balance and boost your SEO, Google’s Lighthouse Reports (How To Run Lighthouse) lays out four best practices that guide this resizer solution using Thumbor and Photo Center:
-
Properly Size Images - Make sure images are not stretched nor shrunk. The rule also audits whether an image’s native aspect ratio is not squeezed from a square into a rectangle.
-
Defer Offscreen Images - Only serve images that are shown to a user.
-
Efficiently Encode Images - Compress images with services like Thumbor.
-
Serve Images In Next-Gen Formats - Allow WebP and more optimal formats to be served, in addition to jpg and png.
Make images load quickly
We’re going to first properly size the image how it’s expected to be shown (for example, put a 100px x 100px image in a 100px x 100px slot). Next, you’ll see how a subtle change by efficiently encoding the image can affect performance using Thumbor.
Once you’ve uploaded images to Photo Center, you will have a link to an image like
https://arc-anglerfish-arc2-prod-corecomponents.s3.amazonaws.com/public/XEVN7EOCEZBN5GOPBWQ5NPSW64.JPG
The raw image URL can be massive (1361px x 761px). If you load an image like this on a page, especially with a slow connection (can change in your Chrome DevTools Network tab), you will see the image load in chunks. We can avoid this by resizing the image with Thumbor:
const resizerKey = 'zzz';const resizerURL = 'https://yoursite.arcpublishing.com/resizer';const targetImageURL = 'https://arc-anglerfish-arc2-prod-corecomponents.s3.amazonaws.com/public/XEVN7EOCEZBN5GOPBWQ5NPSW64.JPG';
const Thumbor = require('thumbor-lite');
const thumbor = new Thumbor(resizerKey, resizerURL);
const targetWidth = 100;const targetHeight = 100;
const imageURLWithoutProtocol = targetImageURL.replace(/(^\w+:|^)\/\//, '');
const resizedImageURL = thumbor .setImagePath(imageURLWithoutProtocol) .resize(targetWidth, targetHeight) .buildUrl();
console.log(resizedImageURL) // https://yoursite.arcpublishing.com/resizer/_urul7GA8_gMewRJumMLOTpxk94=/100x100/arc-anglerfish-arc2-prod-corecomponents.s3.amazonaws.com/public/XEVN7EOCEZBN5GOPBWQ5NPSW64.JPG
If you do this, and place the image in a 100x100 container, the first test will pass Properly size images
. And, if you add one line, the third audit Efficiently encode images
will pass. This is adding compression to the image that is hardly distinguishable.
// google lighthouse judges you against a quality of 85const filterQuality = 85;const resizedImageURL = thumbor .setImagePath(imageURLWithoutProtocol) /* new quality filter */ .filter(`quality(${filterQuality})`) .resize(targetWidth, targetHeight) .buildUrl();
console.log(resizedImageURL); // https://yoursite.arcpublishing.com/resizer/_jdhf7GA8_gfFaaDDDRJumMLOTpxk94=/100x100/filters:quality(85)/arc-anglerfish-arc2-prod-corecomponents.s3.amazonaws.com/public/XEVN7EOCEZBN5GOPBWQ5NPSW64.JPG
This implements widths you pass in through properties and a filter quality you can designate or use default.
Make images load securely
This goes back to to the initial requirement that you need a resizer key as well as a resizer endpoint. We’ll first show how to use those in a secure way that does not expose the key to the client. Next, you’ll see how to test if your key is secure. And, last, how you may need to migrate your solution so that image resizing is not reliant running on the client-side.
For the purposes of the docs, I’ve made the hash between resizer and dimensions different after the filter quality. That’s because that hash is tied to the resizer URL, the image, the filters, the resizer key, and everything else (see URL tampering for more information). If the URL has been tampered with, the resizer will not serve the image with its options.
If we’re following security best practices, it can be helpful as a sanity check to add:
if (!window) { const resizedImageURL = thumbor .setImagePath(imageURLWithoutProtocol) /* new quality filter */ .filter(`quality(${filterQuality})`) .resize(targetWidth, targetHeight) .buildUrl();}
If you don’t, you may notice that unsafe
can appear in the URL of the image if it’s being resized client-side http://some.server.com/unsafe/300x303/smart/path/to/image.jpg
. That means that, if you’re using PageBuilder Engine, a user could find the key and resizer URL in the Fusion global object.
To combat this, content sources (see Content Source API) are not run on the client side. Therefore, if the image resizing takes place there, then the key will not be exposed if the key is encrypted.
Give responsive options
Next you’ll see how to give the browser options based on the size of the screen and other factors for a more responsive solution. That will be the widths
key that will be options for content sources. If you want to use an image width, you need to add it to that list. In this implementation, you will be given back a list of resized options with dimensions and their respective hashes to be recreated with the resizer URL on the client-side. The Image in engine-theme-sdk uses source tags and media queries for mobile, tablet and desktop breakpoints. However, different dimensions of the window can be designated.
All in all, this means the image will be properly sized for mobile as well as desktop widths.
Defer loading the image
This can work out-of-the-box with loading="lazy"
in Chrome, Edge, Firefox, and Opera at the moment of publication (browser support). This can be Polyfilled. It is not currently implemented.
However, there’s other functionality that can be used utilized more dynamically the IntersectionObserver API (browser support).