You drag one photograph into the Media Library. WordPress tells you the upload succeeded and shows you a thumbnail. It looks like one file arrived and one file is now stored.
That is not what happened. Somewhere between the drop and the confirmation, WordPress opened your photograph, resized it seven times, wrote seven new files to disk, and recorded all of them in a database row you will probably never look at. The original is still there too, untouched, and on most sites it will never be sent to a visitor again.
None of this is a bug. It is the mechanism that lets a phone on a slow connection and a 27-inch monitor load the same page and each get a file that suits them. But it is invisible, it compounds, and it explains a whole family of problems that look unrelated: images that are inexplicably soft in one place and sharp in another, a media folder that has quietly grown to twelve gigabytes, a plugin that suddenly shows blurry product thumbnails for old items and perfect ones for new items.
This article is about what actually gets created, why some of it is missing, and what to do about it.
The sizes WordPress creates without being asked
A clean WordPress install with no theme and no plugins registers five image sizes. Three of them are visible in Settings → Media. Two are not, which is the first place people lose track.
thumbnail: 150 × 150, cropped. The only default size that crops. Everything else scales proportionally; the thumbnail is forced into a square, and WordPress decides which part of your picture survives. If your subject is off-centre, this is where it gets beheaded.
medium: 300 px on the longest side. Scaled, not cropped.
medium_large: 768 px wide. This one has no setting in the admin at all. It was added in WordPress 4.4 to feed responsive images, and it exists on your site whether or not you want it.
large: 1024 px on the longest side.
1536×1536 and 2048×2048. Added in WordPress 5.3, again with no admin setting, again for high-density screens. These are the retina counterparts of medium_large and large.
That is seven files, not counting your original. And there is an eighth thing that happens to large uploads, which is the part that confuses people most.
The original you can no longer reach
Since WordPress 5.3, any image wider or taller than 2560 pixels gets a new file appended with -scaled in the name. Upload beach.jpg at 4000 × 3000 and you get beach-scaled.jpg at 2560 × 1920.
From that moment, beach-scaled.jpg is what WordPress treats as the image. It is what the editor inserts, what other sizes are derived from, what the REST API returns. Your 4000-pixel original is still on disk under its original name, but nothing links to it. It sits there being 5 MB.
This is not a rare edge case. The first diagram in this article was uploaded at 1800 × 3320 pixels, and the version you are looking at is -scaled, resized to 2560 on the long edge, because the threshold applies to height just as it does to width.
The threshold is a filter, big_image_size_threshold, and you can raise it, lower it, or switch it off entirely by returning false. Switching it off is tempting and usually wrong: it means every page that inserts a full-size image is handing visitors a multi-megabyte file. Raising it to 3000 or 3500 is the reasonable move if you publish photography and want more detail available.
Why half your sizes are missing
Here is the rule that explains more confusion than any other: WordPress never scales an image up.
A size is only created if the original is larger than the target. Upload a 900-pixel-wide image and you get thumbnail, medium and medium_large. You do not get large, you do not get 1536, you do not get 2048, not as empty files, not as copies, not at all. The database simply has no entry for them.
This has a consequence people run into constantly. When a template asks for the large size and large does not exist for that image, WordPress falls back to the full file. Sometimes that is fine. Sometimes the full file is 300 pixels wide and gets stretched across a 1200-pixel-wide card, and you spend an afternoon convinced your theme is broken.
What themes and plugins add on top
Everything above is core. Almost no real site runs only core.
Any theme or plugin can register its own sizes with add_image_size(), and they do it liberally. A theme with a hero area, a blog grid, a related-posts strip and a gallery might register six. WooCommerce registers three of its own: a product thumbnail, a single-product image and a gallery thumbnail. Email plugins register one for newsletter width. Page builders register their own.
They stack. Nothing consolidates them, nothing notices that two plugins have registered near-identical dimensions, and there is no admin screen listing them.
To see what your site actually registers, this returns the full list including everything themes and plugins have added:
print_r( wp_get_registered_image_subsizes() );
And to see what actually exists for one specific attachment, which is a different question, because of the no-upscaling rule:
print_r( wp_get_attachment_metadata( 1234 ) );
The second one is the honest answer. The first tells you what your site would like to create; the second tells you what it managed to create for that particular file.
What this looks like on a real site
The numbers below come from this website, counted directly in the database rather than estimated.
The Media Library holds 634 images. Those 634 uploads have produced 3,765 files.
The distribution is uneven, and the unevenness is the interesting part. Only 30 images have a 2048-pixel version, because only 30 uploads were ever that large: that is the no-upscaling rule, visible in production. Meanwhile thumbnail and medium exist for almost everything, and the sizes in between fall away steadily as originals get smaller.
The WooCommerce row is the one worth pausing on. WooCommerce registers its three sizes, but registration only affects uploads that happen afterwards. The images that were already in the library when it was activated never got them, and never will, until someone regenerates. On a shop, that is exactly the situation where half the product thumbnails look right and half look soft, and the pattern follows upload date, which is why it seems random.
Which file a visitor actually receives
Having all these sizes would be pointless if the browser could not choose between them. That is what the srcset attribute is for: WordPress lists the available widths, the browser picks one based on the viewport and the screen density, and the file that arrives is usually neither the largest nor the smallest.
The mechanism has its own set of traps, particularly the sizes attribute, which is the part that actually determines whether a browser makes a good choice or a wasteful one. That is a subject of its own, and it is covered in why your images look soft on a retina screen.
For the purposes of this article, one number matters: of the eight or more files sitting on disk for a given image, a single visitor loads at most two: one for the layout, occasionally a second if the layout changes on resize. Everything else is storage.
Reading the list is one thing, watching it happen to your own file is another. Drop an image below, add the sizes your theme registers, and the simulator writes out every file WordPress would create from that one upload, with dimensions, crop behaviour and a size estimate for each.
Upload simulator
Drop one image and see what a WordPress install would really do with it: whether it gets a -scaled copy, which sub-sizes are actually written, what each one costs in bytes, the srcset a browser receives and the file names on disk. Every pixel is decoded and re-encoded in this browser tab, nothing is uploaded anywhere.
No image yet. Everything below starts at WordPress core's own defaults and can be edited.
| Name | Width | Height | Crop | Remove |
|---|
A height of 0 means no limit, the way core registers medium_large as 768 by 0. With crop off a size is a bounding box, with crop on it is exact, cut from the position you pick.
82 and 86 are core's own defaults, one per format. A threshold of 0 switches the big image scaling off, which is what big_image_size_threshold returning false does.
Nothing simulated yet. Drop an image above, or press Try a sample photo to watch a 3000 by 2000 pixel upload go through it.
| Order | Size | Asked for | Written | File | Bytes |
|---|
Core creates medium, large, thumbnail and medium_large first, in that order, so that the sizes a page needs most exist even if the run dies half way. Sub-sizes are cut from the file you uploaded, never from the -scaled copy, which is why they can be wider than it.
| Candidate | Size | In the srcset |
|---|
Deliberate limits: the encoder here is the browser's, not the server's. GD and ImageMagick write different bytes at the same quality number, so the JPEG and WebP figures are indicative, good to a rough tenth, not exact. PNG is worse than indicative and not comparable at all: quality does not apply to it and the browser's PNG writer has nothing to do with GD's. GIF cannot be written by a canvas, so a GIF is measured as PNG and marked as such. Colour profiles and EXIF are dropped by the canvas, a server keeps some of them.
Changing what gets created
Three things are worth doing, in increasing order of impact.
Set the three visible sizes deliberately. Settings → Media. Most people never touch these and end up with sizes that match nothing in their design. If your content column is 720 pixels wide, a 1024-pixel large is sensible and a 300-pixel medium is probably useless to you.
Remove sizes you do not use. This goes in your theme’s functions.php or a small plugin:
add_action( 'init', function () {
remove_image_size( 'woocommerce_gallery_thumbnail' );
remove_image_size( '2048x2048' );
} );
Be careful here. Removing a size a template still asks for means that template falls back to the full file. Check before you remove, not after.
Add sizes that match your actual layout.
add_action( 'after_setup_theme', function () {
add_image_size( 'card', 640, 400, true ); // true = hard crop
} );
The fourth argument is the crop flag. true forces the exact dimensions and discards what does not fit; false scales to fit within the box. For cards and grids where everything must align, you want the crop.
A note on cropping: WordPress decides which part of the image survives, and its default is the centre. For product shots and portraits that is often wrong, and the fix is not code: it is cropping the image deliberately before it ever reaches the Media Library.
Regenerating after a change
None of the above affects images already uploaded. Registration is forward-looking. After changing sizes you either regenerate or live with a library where old and new images behave differently, which, as the WooCommerce numbers above show, is a real and confusing state to be in.
Regenerating means WordPress re-opens every original and re-derives every registered size. The word “re-opens” is doing a lot of work in that sentence, and it is where the problems come from.
Memory is the constraint, not disk or CPU. A JPEG is compressed on disk and uncompressed in memory. A 4000 × 3000 photograph is perhaps 5 MB as a file and roughly 48 MB once PHP has it open as a bitmap: four bytes per pixel, twelve million pixels. During a resize, the source and the target are both in memory at once. A PHP memory limit of 128 MB handles that, but it does not handle it comfortably, and a 6000-pixel original will kill the process outright. The symptom is a regeneration that stops partway with no useful error, or a white screen.
Before starting anything on a large library, check what you actually have:
echo ini_get( 'memory_limit' ); // PHP's ceiling
echo WP_MEMORY_LIMIT; // what WordPress asks for
256 MB is a sensible working figure. If your host caps you lower and will not move, the answer is not to force it: it is to reduce the size of the originals first.
Do it in the right order. Regenerating is the expensive step, so everything that reduces work should happen before it, not after: delete images you do not need at all; remove sizes you do not use with remove_image_size(), so you are not faithfully recreating three shop sizes on 600 images that will never appear in a shop; decide on big_image_size_threshold now, because changing it afterwards means regenerating again. Then regenerate.
Doing this in the reverse order is the common mistake, and it turns a five-minute job into two five-minute jobs plus a lot of files you then delete.
Expect it to take longer than you think. A few hundred images is a couple of minutes. Ten thousand is not ten times longer: it is longer than that, because larger libraries tend to contain larger originals. Run it from WP-CLI if you have shell access, because a browser tab that times out mid-run leaves you with a partially regenerated library and no record of where it stopped:
wp media regenerate --yes
wp media regenerate --only-missing --yes # skips what already exists
The --only-missing flag is the one to reach for when you have added a size rather than changed one. It skips every attachment that already has all its registered sizes, which on a library like the one measured above means it touches roughly half the images instead of all of them.
One thing regenerating cannot fix. If your original was too small to begin with, no amount of regeneration creates the missing large sizes: the no-upscaling rule still applies. Those images need to be re-uploaded at a higher resolution or left alone. Regeneration is not a repair tool; it only re-derives what the original can support.
This is also the moment to ask whether you want to regenerate everything at all. If the library has never been audited, there is usually more to clean up than sizes alone: orphaned files with no attachment record, duplicates uploaded three times under slightly different names, and sizes belonging to plugins that were removed years ago and never cleaned up after themselves.
When something looks wrong: symptoms and causes
Most of the questions people ask about image sizes do not arrive as questions about image sizes. They arrive as a specific thing looking wrong. Here are the common ones and what is usually behind them.
An image is sharp in the post but soft in the archive listing, or the other way round. The two places request different sizes. One of them is requesting a size that does not exist for that image, and WordPress is falling back to whatever is available: often the full file, scaled down by CSS, or a small size stretched up. Check wp_get_attachment_metadata() for that specific attachment and see which sizes are actually there.
Product thumbnails look right for new items and soft for old ones. Classic registration-order problem. The theme or plugin registered its sizes after those older images were uploaded, so the sizes were never created for them. Regenerate with --only-missing.
A picture is beheaded, or the wrong part of it is showing. That is the hard crop, and WordPress chose the centre. Either register that size with false as the crop argument so it scales instead, or crop the image deliberately before uploading. There is no setting for “crop toward the top”.
The Media Library is enormous and you cannot account for it. Multiply your upload count by the number of registered sizes and you usually have your answer before you look at anything else. Then check for a -scaled situation: every large upload is storing both a full original nothing links to and a 2560 working copy.
A newly uploaded image looks worse than the file you uploaded. WordPress re-encodes at 82% JPEG quality by default. If you uploaded an already-compressed JPEG, it has now been compressed twice. Raise it with the jpeg_quality filter, or upload PNG for graphics and screenshots where re-compression is visible: the format decision matters more than most people assume.
Uploads fail entirely on large files. Different problem, same root cause as the memory issue above: the resize happens during upload, and if PHP runs out of memory mid-resize, the upload fails after the file has already transferred. The file size limit shown in the uploader is about transfer, not about processing: a file can be well under the limit and still fail.
An image displays at the right size but the file is huge. The browser chose a large candidate from srcset because the sizes attribute told it the image would be displayed wider than it actually is. This is the single most common responsive-images mistake and it is worth understanding properly.
Choosing dimensions that hold up
A few rules that survive contact with real projects.
Upload larger than you need, but not enormous. Something between 2000 and 2560 pixels on the long edge gives WordPress room to derive every size, including the retina ones, without leaving a 5 MB original on disk forever.
Match your sizes to your breakpoints, not to round numbers. If your grid switches to two columns at 768 and your cards are then 360 pixels wide, a 640-pixel card size covers both the normal and the retina case. A 1200-pixel card size is waste in every scenario.
Decide the crop before the upload. Every automated crop is a guess about what matters in your picture. The guess is centre.
Choose the format with intent. Sizes are only half the weight question; the encoding is the other half.
Where this leaves you
The mechanism is not complicated once it is visible. One upload becomes eight or more files. Sizes appear only when the original was big enough. Themes and plugins add their own, silently, and only for images uploaded after they were activated. A visitor sees at most two of the files that exist.
What makes it feel complicated is that all of it happens with no interface. There is no screen in WordPress that shows you which sizes are registered, which ones exist for a given image, or how much disk they occupy: the information is in the database, and you have to go and get it.
That gap is the reason WunderPaint has a Media Library Manager: registered sizes, actual files per attachment, duplicates and orphans, listed rather than inferred. It also means you can crop deliberately before an upload rather than letting the centre crop decide, and set alt text while you are in there.
The plugin is free and stays free. But the more useful takeaway is the query itself: run wp_get_registered_image_subsizes() on your own site and see how many sizes you are creating on every single upload. On most sites the number surprises people.