WordPress Images: Fixing Sizes, Formats and Lazy Loading
WordPress does a lot with images before you install anything: it makes several copies of every upload, writes srcset and sizes, lazy-loads images and marks a likely main image as high priority. Most image findings on WordPress sites come from that system being bypassed or fighting with something else.
A theme hard-codes full-size images, two plugins both lazy-load, a page builder ignores the registered sizes, or old uploads never got modern formats. This guide goes through the WordPress settings, plugins and code that fix each Image Optimisation finding, and how to apply the fixes to thousands of existing uploads safely.
What WordPress already does with images
Knowing the built-in behaviour saves you from installing plugins to do things WordPress already does, and helps you spot where it has been switched off.
- Several sizes per upload. Each image is copied into the sizes set under Settings,
then Media (thumbnail, medium and large), plus medium_large at 768 pixels wide, two larger sizes added
in version 5.3, and any sizes your theme or plugins register with
add_image_size(). - A cap on huge uploads. Since 5.3, images wider or taller than 2560 pixels are scaled
down and saved with
-scaledin the file name, with the original kept. The limit is adjustable with thebig_image_size_thresholdfilter. - Responsive markup. Images output through the editor or
wp_get_attachment_image()getsrcset,sizes,widthandheightautomatically. - Lazy loading and priority. Core adds
loading="lazy"to images, and in recent versions tries to leave the first images eager and addsfetchpriority="high"to the one it judges most likely to be the Largest Contentful Paint element. - Modern format uploads. WebP uploads are supported from 5.8 and AVIF from 6.5, provided the server's image library can handle them.
- Compression. Generated sizes are re-encoded, with a default JPEG quality of 82 that
can be changed with the
wp_editor_set_qualityfilter.
Which WordPress cause sits behind each finding
| Audit finding | Common WordPress cause |
|---|---|
| Oversized dimensions | Theme outputs the full size or a hard-coded URL; no
registered size close to the display width; sizes assumes a wider column than the theme
uses. |
| Oversized file | Originals uploaded straight from a camera or phone; no compression plugin; quality set too high. |
| Old format | No WebP or AVIF generation, or a plugin that creates WebP but never serves it. |
| Main image lazy-loaded | An optimisation plugin's script-based lazy loading on top of core; a page builder hero not recognised by core. |
| Missing dimensions | Images inserted as raw HTML, from old posts, or by builders and sliders that skip core functions. |
| Slow delivery | No CDN; images served from shared hosting far from visitors; cache headers not set for the uploads folder. |
The fix, step by step
Step 1
Check what the audit and Site Health report
Run the Image Optimisation audit on the home page, a typical post, a category archive and, on WooCommerce sites, a product page. Note which findings repeat across templates, because those come from the theme or a plugin rather than individual uploads. Then open Site Health, Info, Media Handling to see which image library and formats the server supports. Take a full backup of the database and the uploads folder before changing anything that rewrites images.
Step 2
Match registered sizes to the theme
Measure
the widths your theme actually displays images at: content column, featured image, archive card and sidebar.
Set the medium and large sizes in Settings, Media to values near those widths, and register any missing
ones in a child theme with add_image_size(). Remove sizes registered by plugins or old themes
you no longer use, using the intermediate_image_sizes_advanced filter, because each extra
size adds files to every upload without helping any page.
Step 3
Cap upload size and set compression quality
Keep the big image threshold on, and lower it with the big_image_size_threshold
filter if your widest layout is well under 2560 pixels, for example to 2048. Ask editors to export photos
at a sensible size before uploading, and consider a plugin option that resizes originals on upload. If
you run a compression plugin, let it control quality rather than also changing wp_editor_set_quality,
so images are not compressed twice with different settings.
Step 4
Choose one optimisation plugin and configure it
Plugins such as ShortPixel, EWWW Image Optimizer, Smush and Imagify compress uploads and can create modern formats, some on your server and some through a cloud service. Pick one, deactivate the others, and check whether it compresses all generated sizes, not just the original. Decide whether to keep a backup of originals, which uses storage but lets you undo. Switch off the plugin's own lazy loading unless you are sure core lazy loading is disabled.
Step 5
Serve WebP or AVIF
Creating
modern files is only half the job; the page must request them. Plugins do this in one of three ways:
rewriting image tags into picture elements, using server rewrite rules that swap files by
the browser's Accept header, or pointing images at a CDN that converts on the fly. Rewrite
rules need Apache or nginx configuration and a Vary: Accept header. The Modern Image Formats
plugin from the WordPress performance team is another option, generating WebP or AVIF sub-sizes on upload.
Step 6
Regenerate sizes for existing uploads
New
size settings and format options only apply to future uploads. To update the existing library, run wp media regenerate --only-missing
with WP-CLI, which creates missing sizes without redoing ones that exist, or use a regeneration plugin
in batches. On large libraries, run it at a quiet time and watch disk space. Afterwards, run the optimisation
plugin's bulk tool so new sizes are compressed and converted, then spot-check a few old posts.
Step 7
Sort out lazy loading and the main image
View the source of a page and check the first large image: it should have no loading="lazy"
and ideally fetchpriority="high". If a plugin adds script-based lazy loading, look for data-src
attributes and turn that feature off, or add the hero's class to the plugin's exclusion list. For builder
heroes core does not recognise, set the image to load eagerly in the widget settings, or filter wp_get_loading_optimization_attributes
in a child theme.
Step 8
Fix templates and builders that bypass core
Search the theme files for wp_get_attachment_url or the_post_thumbnail_url
inside hand-built img tags, and replace them with wp_get_attachment_image()
or the_post_thumbnail() with a named size, so srcset, sizes and
dimensions return. In page builders, choose a registered size in each image widget instead of Full. For
old posts with raw HTML images, re-insert key images or use the editor's Convert to blocks option.
Child theme snippets
Add these to a child theme's functions.php or a small site-specific plugin, never to
the parent theme, where updates would remove them. The example sizes suit a fictional theme for Harbourside
Bakery with a 760 pixel content column and 400 pixel archive cards.
<?php
// Sizes the theme actually uses.
add_action( 'after_setup_theme', function () {
add_image_size( 'card', 400, 300, true ); // archive cards, cropped
add_image_size( 'card-2x', 800, 600, true ); // cards on high-density screens
} );
// Lower the big image cap from 2560 to 2048 pixels.
add_filter( 'big_image_size_threshold', function () {
return 2048;
} );
// Stop generating sizes nothing on the site uses.
add_filter( 'intermediate_image_sizes_advanced', function ( $sizes ) {
unset( $sizes['1536x1536'], $sizes['2048x2048'] );
return $sizes;
} );
// Describe the real content column in sizes for post images.
add_filter( 'wp_calculate_image_sizes', function ( $sizes, $size ) {
return '(min-width: 800px) 760px, calc(100vw - 2rem)';
}, 10, 2 );
Be careful with the last filter: it changes sizes for every image output through core.
If your templates have images at several widths, check the $size argument or apply the rule
only in content, and give cards and headers their own values when you output them.
Outputting a featured image correctly
<?php
// Before: hard-coded full-size URL, no srcset, no dimensions.
echo '<img src="' . esc_url( get_the_post_thumbnail_url( null, 'full' ) ) . '">';
// After: core builds srcset, sizes, width, height and loading.
the_post_thumbnail( 'large', array(
'sizes' => '(min-width: 800px) 760px, calc(100vw - 2rem)',
) );
Regenerating with WP-CLI
# Create missing sizes for all images, skipping ones already present
wp media regenerate --only-missing --yes
# Or regenerate only images attached in the last batch of IDs
wp media regenerate 1201 1202 1203 --yes
Choosing between optimisation plugins
The popular plugins overlap heavily, and their feature lists and plans change, so compare the current versions rather than relying on old reviews. The questions that actually separate them for your site are these:
- Where does compression happen? Local processing uses your server's CPU and image library, which can be slow on cheap shared hosting and limited by the formats the library supports. Cloud processing is lighter on the server but usually metered by credits or plan.
- How are modern formats delivered? Through
picturetags, server rewrites or a CDN. Each works; the right one depends on your hosting and caching setup. Rewrites and CDNs keep the HTML simple, whilepicturetags work on almost any host. - Does it touch every size? Some plans or settings compress only the original or only a limited number of sizes. Check the setting that controls thumbnails.
- Can you undo it? Keeping originals lets you re-compress later at a different quality, at the cost of disk space.
- What else does it do? Many plugins bundle lazy loading, CDNs or CSS optimisation. Turn off overlapping features so only one plugin does each job.
Hosting matters too. Some managed WordPress hosts compress images or serve them through their own CDN, and some block certain plugins. Check your host's documentation before adding a plugin that duplicates a service you already have.
WooCommerce and page builders
WooCommerce
WooCommerce registers its own sizes for product thumbnails, the single product image and gallery thumbnails, set under Appearance, Customise, WooCommerce, Product Images on classic themes, or declared by block themes. After changing them, regenerate thumbnails. The single product image is usually the main image on product pages, so check it is not lazy-loaded and that gallery images after the first are. Zoom and lightbox features load the full-size file; keep those originals reasonably sized.
Elementor, Divi and other builders
Builders have their own image widgets and background settings. Choose a registered size in each image
widget rather than Full, and check whether the builder adds its own lazy loading on top of core. Section
backgrounds set in a builder are CSS backgrounds, so they get neither srcset nor native
lazy loading; for a hero, an image widget is usually faster than a background.
Edge cases
- Images in the uploads folder served without caching. Check that your server or CDN
sends a long
cache-controlfor/wp-content/uploads/. See the image CDN guide for headers. - Multisite. Size settings and plugins may be network-wide or per site. Regenerate
per site with the
--urloption in WP-CLI. - Offloaded media. If uploads are moved to cloud storage by a plugin, regeneration and conversion must happen before offloading, or through the offload plugin's own tools.
- Images in custom fields. Fields that store a URL rather than an attachment ID cannot
produce
srcset. Change the field to return the image ID and output it withwp_get_attachment_image(). - Headless WordPress. When a front end such as Next.js renders the site, WordPress's image markup is not used. Image fixes move to the front end; see our Next.js images guide.
Keeping a WordPress media library healthy
- Give editors a short checklist: export photos no wider than your largest layout needs, name files descriptively, and choose the right size when inserting.
- Keep one owner per job: one plugin for compression and formats, core or one plugin for lazy loading, one CDN.
- Review after theme changes: a new theme brings new column widths and new registered sizes, so regenerate and re-audit.
- Check plugin updates: image and performance plugins sometimes switch features on in updates. Re-run the Image Optimisation audit on key templates after updating them.
1. Which image optimisation plugin is best for WordPress?
There is no single best choice, because the right plugin depends on your hosting and how you want modern formats delivered. ShortPixel, EWWW Image Optimizer, Smush and Imagify all compress images and can create WebP, and several can create AVIF. Compare where compression happens, how formats are served, whether all sizes are processed and whether originals are kept. Whatever you choose, run only one and switch off features that overlap with other plugins.
2. Does WordPress convert images to WebP automatically?
Not by default in every setup. WordPress accepts WebP uploads from version 5.8 and AVIF from 6.5 if the server supports them, and developers can use the image_editor_output_format filter to save generated sizes in another format. The Modern Image Formats plugin from the WordPress performance team, or an optimisation plugin, is the usual way to convert JPEG and PNG uploads. Check Site Health to confirm your server's image library supports the format.
3. Why does WordPress create so many copies of each image?
Each copy is a different width, so browsers can download the one closest to the space the image fills. WordPress creates its default sizes, the extra large sizes added in version 5.3, and every size registered by your theme and plugins. Most sites carry sizes they never use, left over from old themes or plugins. Remove those with a filter, keep the ones your layout needs and regenerate thumbnails so the library matches.
4. How do I regenerate thumbnails without breaking my site?
Take a full backup first. Then use WP-CLI with wp media regenerate and the only-missing option, which adds missing sizes without recreating existing files, or a regeneration plugin that works in batches. Run it at a quiet time, because it is heavy on the server, and watch disk space. Pages that reference the old files keep working, since existing file names do not change unless you remove sizes entirely. Spot-check a few old posts afterwards.
5. Why is my WordPress hero image still lazy-loaded?
Core WordPress tries to leave the first images eager, but its guess depends on the theme outputting images through core functions in the expected order. An optimisation plugin with its own script-based lazy loading, a page builder hero or a slider can bypass that logic. Look at the page source for loading lazy or data-src on the hero, then exclude it in the plugin, change the widget setting or filter the loading attributes in a child theme.
6. Should I reduce the JPEG quality WordPress uses?
WordPress re-encodes generated sizes at a default JPEG quality of 82, which is a reasonable balance for most photos. If you use a compression plugin, let the plugin set quality instead, so images are not compressed twice with different settings. If you do not, lowering the value slightly with the wp_editor_set_quality filter can save bytes, but check photos with fine detail or gradients for visible banding before applying it across the site.
7. Do I need a CDN for WordPress images?
Not always, but it often helps when visitors are spread across countries or your host is slow to serve static files. Many optimisation plugins and hosts include a CDN option, and a whole-site CDN in front of WordPress caches the uploads folder without any plugin. Whatever you use, make sure images get a long cache lifetime, that only one layer converts formats, and that the CDN does not block search engine crawlers.
The Image Optimisation audit checks every image on your page for file size, format, dimensions, alt text, lazy-loading, layout shift and CDN delivery, and ranks its recommendations by their effect on loading and bandwidth.
Open Image Optimisation Fixes โ