Making images fly on
WordPress with Image CDNs
Alistair Shepherd - Front-End Developer
SeriesEight
Images on the web
<img
src="/our-image.jpg"
alt="our image"
>
Images change size depending on screen width
HTML Responsive Images
<img
src="/wp-content/uploads/our-image.jpg"
srcset="
/wp-content/uploads/our-image-300x200.jpg 300w,
/wp-content/uploads/our-image-768x512.jpg 768w,
/wp-content/uploads/our-image-1024x683.jpg 1024w,
/wp-content/uploads/our-image-1536x1024.jpg 1536w
"
sizes="(min-width: 800px) 768px, 100vw"
...
>
Optimisation and Image Format
Size | |
---|---|
Source JPEG | 300kB |
Optimised JPEG | 60kB |
WebP 🙂 | 36kB |
AVIF * 😀 | 28kB |
JPEG XL * 😍 | 24kB |
Size | Chrome | Firefox | Safari | |
---|---|---|---|---|
Source JPEG | 300kB | yes | yes | yes |
Optimised JPEG | 60kB | yes | yes | yes |
WebP 🙂 | 36kB | yes | yes | recent |
AVIF * 😀 | 28kB | yes | recent | no |
JPEG XL * 😍 | 24kB | no | no | no |
Size | |
---|---|
Source JPEG | 300kB |
Optimised JPEG | 60kB |
WebP 🙂 | 36kB |
AVIF * 😀 | 28kB |
JPEG XL * 😍 | 24kB |
Optimisation and Image Format
Hi DPI displays
<picture>
<source
media="(-webkit-min-device-pixel-ratio: 1.5)"
srcset="https://example.com/images/our-image_width1200_quality40.jpg 1200w"
sizes="...">
<img
srcset="https://example.com/images/our-image_width600_quality80.jpg 600w"
sizes="..."
alt="">
</picture>
iPhone 12 Pro (right) screen:
- Screen width: 1170 pixels
- 'Viewport' width: 390 pixels
- Device Pixel Ratio of 3
The browser could decide to load an image with resolution 3x larger than it's displayed - 9x increase in file size!!!
We can mitigate increase in file size by increasing the image compression without visible effect.
Lazy Loading and Decoding
- "loading" attribute set to "lazy" or "eager" depending on position on page
- decoding="async" when images shouldn't delay text rendering
<img
loading="lazy"
decoding="async"
>
<picture>
<source
type="image/avif"
media="(-webkit-min-device-pixel-ratio: 1.5)"
srcset="/assets/image/otter-300-q40.avif 300w, /assets/image/otter-450-q40.avif 450w, /assets/image/otter-600-q40.avif 600w,
/assets/image/otter-800-q40.avif 800w, /assets/image/otter-1000-q40.avif 1000w, /assets/image/otter-1200-q40.avif 1200"
sizes="(min-width: 87rem) 40rem, (min-width: 768px) calc(50vw - 4rem), calc(100vw - 2rem)">
<source
type="image/avif"
srcset="/assets/image/otter-300-q80.avif 300w, /assets/image/otter-450-q80.avif 450w, /assets/image/otter-600-q80.avif 600w,
/assets/image/otter-800-q80.avif 800w, /assets/image/otter-1000-q80.avif 1000w, /assets/image/otter-1200-q80.avif 1200"
sizes="(min-width: 87rem) 40rem, (min-width: 768px) calc(50vw - 4rem), calc(100vw - 2rem)">
<source
type="image/webp"
media="(-webkit-min-device-pixel-ratio: 1.5)"
srcset="/assets/image/otter-300-q40.webp 300w, /assets/image/otter-450-q40.webp 450w, /assets/image/otter-600-q40.webp 600w,
/assets/image/otter-800-q40.webp 800w, /assets/image/otter-1000-q40.webp 1000w, /assets/image/otter-1200-q40.webp 1200"
sizes="(min-width: 87rem) 40rem, (min-width: 768px) calc(50vw - 4rem), calc(100vw - 2rem)">
<source
type="image/webp"
srcset="/assets/image/otter-300-q80.webp 300w, /assets/image/otter-450-q80.webp 450w, /assets/image/otter-600-q80.webp 600w,
/assets/image/otter-800-q80.webp 800w, /assets/image/otter-1000-q80.webp 1000w, /assets/image/otter-1200-q80.webp 1200"
sizes="(min-width: 87rem) 40rem, (min-width: 768px) calc(50vw - 4rem), calc(100vw - 2rem)">
<source
media="(-webkit-min-device-pixel-ratio: 1.5)"
srcset="/assets/image/otter-300-q40.jpg 300w, /assets/image/otter-450-q40.jpg 450w, /assets/image/otter-600-q40.jpg 600w,
/assets/image/otter-800-q40.jpg 800w, /assets/image/otter-1000-q40.jpg 1000w, /assets/image/otter-1200-q40.jpg 1200"
sizes="(min-width: 87rem) 40rem, (min-width: 768px) calc(50vw - 4rem), calc(100vw - 2rem)">
<img
alt="otter standing on a log looking out majestically"
src="/assets/image/otter-1200-q80.jpg"
srcset="/assets/image/otter-300-q80.jpg 300w, /assets/image/otter-450-q80.jpg 450w,
/assets/image/otter-600-q80.jpg 600w, /assets/image/otter-800-q80.jpg 800w,
/assets/image/otter-1000-q80.jpg 1000w, /assets/image/otter-1200-q80.jpg 1200"
sizes="(min-width: 87rem) 40rem, (min-width: 768px) calc(50vw - 4rem), calc(100vw - 2rem)"
width="1200"
height="784"
loading="lazy"
decoding="async"
>
</picture>
That's a lot going on!
Image processing wishlist
- Convert to several different formats, update to support new ones like AVIF and JPEG XL for browsers that support them
- Resize images to many different sizes for responsive devices
- Handle differing quality levels depending on density
- Bonus: Perform quality analysis comparing image sizes between different formats
- Optimise images and compress where possible
To display a few images on a webpage!
Images on WordPress
WordPress image sizes
- Thumbnail: Crop to 150 x 150 px
- Medium: Fit within 300 x 300 px
- Large: Fit within 1024 x 1024 px
- Full: original uploaded size
WordPress wishlist
- Convert to several different formats, update to support new ones like AVIF and JPEG XL
- Resize images to many different sizes
- Handle differing quality levels depending on density
- Bonus: Perform quality analysis comparing image sizes between different formats
- Optimise images and compress where possible
Bespoke theme development
<?php
add_image_size('image-size-name', 1000, 600, true);
Applies to all images, not just those that need it!
Plugins to help
- Conversion: WebP Express, Imagify, AVIF?
- Resizing: many different plugins
- Quality levels: can't find any
- Quality analysis: definitely not
- Optimisation: Resmush.it, EWWW, Shortpixel, WP Smush
HTTP Archive data
Source and image credit:
HTTP Archive Web Almanac, 2021 - CMS Chapter
WordPress image issues in the wild
- Fresh redesign and rebuild as a bespoke WordPress Theme
- Very visual featuring lots of images
- Was in a reasonably rural area so we wanted to ensure performance was very good
- Around 15 different services
- Up to 20-30 pictures of products for each service
- Hundreds of high-quality images
Implemented with WordPress
- Used WebP Express for WebP conversion, it worked but
wasn't ideal
- Added many custom image sizes with add_image_size()
- Handle differing quality levels depending on density
- Bonus: Perform quality analysis comparing image sizes between different formats
- Used Resmush.it for image optimisation
Results
- Created a lot of additional costs of servers, services and storage
- Degraded WordPress editor experience
- Created technical debt
- In the end we removed a lot of it to make maintenance easier and the editing experience better
- At first it was okay, improved performance slightly
Image
CDNs to
the rescue
What is an Image CDN?
- Content Delivery Network (CDN) specialising in image processing
- Sits between the web server of your site and the user, 'transforming' images on the fly by changing image URLs
- Third-party services selling solutions to make dev experience of working with images easier and improving performance
and many more
Web Server
HTML
CSS
JS
Images
Images
User's browser
Optimised
Source
Web Server
User's browser
HTML
CSS
JS
Images
Images
Web Server
Optimised
Source
Web Server
User's browser
HTML
CSS
JS
Images
Images
Media Storage
How do we use them?
https://example.imagecdn.com
Start with our image
CDN domain
https://example.imagecdn.com
/https://oursite.com
/wp-content/uploads
/example.jpg
Add the URL to our
source image
https://example.imagecdn.com
/example.jpg
Many CDN providers allow
you to make presets/aliases
https://example.imagecdn.com
/example.jpg
?w=600&h=400
Add parameters to
'transform' the image
https://example.imagecdn.com/fetch
/example.jpg
/w_600,h_400
Different CDNs have different formats,
but work on the same concepts
- Handled separately from your main site
- Managed by image experts
- Automatic format detection
.../w_300,h_600,c_thumb,g_auto/...
Tall banner with automatic gravity
.../w_1000,h_400,c_fill,g_north,
e_colorize:30,co_rgb:dd14d1/...
Wide banner anchored to top, coloured pink
Implementing
Image CDNs
Changing Image URLs
(simplest)
Changing the setting upload_url_path
within database table wp_options
will change the image domain, allowing you to set it to your image cdn
Offload to cloud storage
You could also move your uploads to cloud storage which has benefits:
- reliability of storage;
- making local development easier;
- reducing server load;
- makes moving website easier.
Check out the plugins:
- WP Offload Media
- Humanmade S3 Uploads
Image CDN Plugin
(easiest)
Depending on Image CDN provider there are likely both official and unofficial plugins that make integration very easy and might give lots of bonus features
Image courtesy of Cloudinary WordPress plugin
Custom Theme Markup
(best performance)
<?php
echo image( [
'image' => get_field('image'),
'srcset' => [300, 450, 600, 800, 1000, 1200],
'sizes' => '100vw',
'loading' => 'lazy'
'class' => 'image-class'
] );
accud.io/imagecdn-acf
Image CDNs make all images easy - not just for WordPress!
Moving to using an Image CDN
What we did
- Used the CloudImage Image CDN
- Set up a custom code solution with ACF like the previous example
- Removed custom WordPress image sizes and WebP conversion
Lighthouse performance score improved by ~25
Increased speed of development
Significantly reduced server load and costs
No free lunch!
Cost
- As an external service, there is a cost implication
- Many have a generous free tier, small site might not cost anything
- Larger sites will cost, pricing model depends on the provider
- Unless you have hundreds of thousands of images, likely cheaper than developers maintaining image processing or faffing with plugins
- Can be offset by reduced server load and storage requirements
- Disable CDN for local development
- Offload images to cloud storage
- Push images to your site when needed
Local dev
For very high performing sites different origin has an impact
Solution for nginx
location /cdn/ {
proxy_pass https://example.imagecdn.com/;
}
The scores are in...
Improve the performance of images on your websites
Make working with images easier during development
Reduce server requirements
Try an Image CDN in your next project!
Thank You!
These slides, my sources and helpful links on image CDNs available at accud.io/wordpress-imagecdns
On twitter and most social media @accudio
My website and blog posts alistairshepherd.uk
Making images fly on WordPress with Image CDNs
By Alistair Shepherd
Making images fly on WordPress with Image CDNs
- 457