Why WordPress Images Look Blurry After Upload (and the Fix)
Nine times out of ten your images aren't actually blurry — WordPress downscaled them on upload and is now serving the capped copy. Since WordPress 5.3, any
Published
Nine times out of ten your images aren’t actually blurry — WordPress downscaled them on upload and is now serving the capped copy. Since WordPress 5.3, any image wider or taller than 2560px gets silently replaced by a -scaled version maxed at 2560px on its longest edge, and that smaller file is what your theme stretches to fill a hero, full-width block, or retina display. The fix is to prove that’s what happened, then either raise the threshold or stop uploading images that need to be larger than the box they sit in.
Why it happens
When you upload a photo, WordPress runs wp_create_image_subsizes() (in wp-admin/includes/image.php). If the longest edge is over the big_image_size_threshold — 2560px by default — it calls _wp_image_meta_replace_original(), generates your-photo-scaled.jpg, and rewrites the attachment metadata so the full size now points at the scaled copy. Your true original is kept on disk as your-photo.jpg and referenced by wp_get_original_image_path(), but nothing on the front end serves it. Every intermediate size and every srcset candidate is generated from the 2560px scaled file, not from what you uploaded.
So here’s the blur. You upload a crisp 4000px image for a full-bleed header. WordPress caps it at 2560px. On a standard display in a 1600px-wide container, that’s fine. On a 2x retina screen the browser needs roughly 3200 real pixels to fill that container sharply, only has 2560, and upscales the difference. Soft edges, mushy text-in-image, exactly the “blurry after upload” symptom.
The other half of the same problem is srcset picking a size that’s too small. If your theme reports a content width that doesn’t match the actual rendered box, WordPress hands the browser a -1024x683 or -1536x1024 intermediate and it gets stretched up. Same cause — display box larger than the pixels available — different trigger.
Check what WordPress actually served
Don’t guess. Right-click the blurry image, open it in a new tab, and look at the filename in the URL. If it ends in -scaled.jpg or a dimension suffix like -1024x683.jpg, you’ve confirmed it: the browser is rendering a downscaled derivative, not your upload. You can also inspect the element and read currentSrc to see exactly which candidate the browser chose.
To see the whole picture before you re-upload anything, drop the image into the WordPress image sizes tool — it shows every derivative WordPress will generate from that exact file, including whether a -scaled copy gets created and at what dimensions, so you know upfront whether your source is big enough for where you’re placing it.
On the server you can list every capped file:
find wp-content/uploads -name '*-scaled.*'
How to fix it, in order
1. If your images legitimately need to be larger than 2560px (large heroes, retina, print-quality galleries), raise the threshold:
// functions.php or a small mu-plugin
add_filter( 'big_image_size_threshold', function () {
return 3840; // longest edge, in pixels
} );
Or turn the cap off entirely:
add_filter( 'big_image_size_threshold', '__return_false' );
2. Re-upload the affected images. This is the step people skip. Changing the threshold only affects new uploads. Images already in your library keep their old metadata, and regenerating thumbnails rebuilds the intermediate sizes from the capped -scaled file — it does not add back resolution that was thrown away. The reliable path is: change the filter, delete the attachment, upload again.
3. If your source is already ≤2560px and still blurry, the threshold isn’t your problem — the display box is bigger than the image. Upload a larger source, or shrink the container. For retina sharpness, your source needs to be roughly twice the CSS pixel width of the box it fills.
4. Only after the above, regenerate thumbnails so intermediate sizes are consistent — but understand it’s cleanup, not a cure.
What not to do
Don’t crank JPEG quality to 100. WordPress compresses to a default quality of 82 via wp_editor_set_quality, and half the advice online tells you to override it:
add_filter( 'wp_editor_set_quality', function () { return 90; } );
That’s fine for reducing compression artifacts, but a stretched, upscaled image is a resolution problem, not a compression one. Going from 82 to 100 bloats your files and does almost nothing for the softness you’re seeing.
Don’t disable -scaled as a reflex. Turning off the cap (there’s a tool for that) is the right call if you serve images above 2560px — but if your real issue is a too-small intermediate getting stretched, disabling it changes nothing and you lose the bandwidth savings for no reason. Diagnose first.
Don’t blame the cache or regenerate thumbnails first. Regeneration reads from the same capped source, so it can’t restore detail. Clearing a CDN or page cache won’t touch a file that was small on disk to begin with.
Don’t reach for the theme’s CSS or object-fit. No amount of styling adds pixels. If the served file ends in -scaled or a dimension suffix, the file is the problem, not the layout.
Still stuck?
If you’ve confirmed the served file is your full-resolution upload — no -scaled, no dimension suffix — and it’s still soft, check whether a plugin (an optimizer, a CDN image-resizer, or a lazy-load library) is rewriting your src to a compressed proxy. Run the image through the image sizes tool to confirm what WordPress should be generating, then compare that against what the browser actually loads. When those two disagree, something between WordPress and the browser is the culprit.