# Does WordPress compress images? What WordPress 7.1 does to your upload

**Author:** Mozex | **Published:** 2026-10-05 | **Tags:** JPEG, PNG | **URL:** https://www.iminify.com/blog/10-does-wordpress-compress-images-what-wordpress-71-does-to-your-upload

---

We uploaded the same photo to a fresh WordPress 7.1.2 site twice, both times from Chrome. The file we sent, 832 KB, was stored byte for byte both times. From it WordPress made five resized copies at JPEG quality 82. Through Media > Add Media File those copies came to 691 KB. Through the Upload button on an Image block they came to 640 KB, because in 7.1 that button has the browser make the copies instead of the server.

So does WordPress compress images automatically? Not the file you upload, which is stored byte for byte. It re-encodes the resized copies it makes, the JPEG ones at quality 82. In our test a phone and a standard laptop screen downloaded copies of this photo, but a 2x laptop screen got the untouched original, so compressing it is still your job. Since 7.1, which program makes the copies depends on the button you press and the browser you press it in.

<!--more-->

Our test site ran WordPress 7.1.2 in Docker (PHP 8.3, ImageMagick 7.1.1), and we drove it from headless Chrome 153. For the server path we used WP-CLI's `wp media import` in the same container; the 1800 px photo from the opening, sent through Media > Add Media File, got the same copies byte for byte. KB here means 1,024 bytes. The photo is Alexey Topolyanskiy's, on Unsplash, and the screenshot is from Wikipedia; both are in [our test set](https://www.iminify.com/blog/1-ssimulacra-2-scores-in-practice-what-50-70-and-90-cost-in-kilobytes#run-it-yourself).

## Your upload is stored byte for byte, and three cases add a new full size

We compared every stored original with the file we uploaded, by SHA-256. Identical every time, on both paths, for JPEG, PNG and AVIF. WordPress's own code ([`wp_create_image_subsizes()`](https://github.com/WordPress/wordpress-develop/blob/7.1.2/src/wp-admin/includes/image.php)) only writes a new "full" file in three cases:

1. **The image is over 2,560 pixels on either side.** WordPress scales it to fit 2,560, saves it at quality 82 with `-scaled` in the name, and uses that as the full size. The original stays on disk. Returning `false` from the `big_image_size_threshold` filter turns the scaling off. The photo's 6000x4000 version, a 5.0 MB download from Unsplash, got a 2560x1707 `-scaled` file of 820 KB from the server and 927 KB from the browser.
2. **Its EXIF orientation isn't 1.** WordPress saves a rotated copy with `-rotated` in the name.
3. **It's a HEIC or HEIF.** Since WordPress 6.7 those are converted to JPEG. No other format is converted by default ([`media.php`](https://github.com/WordPress/wordpress-develop/blob/7.1.2/src/wp-includes/media.php)).

We uploaded a JPEG carrying GPS coordinates: both stored originals kept them, and so did every copy made on the server. The browser's copies kept none of them.

## Every JPEG copy is re-encoded at quality 82

WordPress registers a cropped 150x150 thumbnail and five more sizes: 300, 768, 1024, 1536 and 2048, each a limit on both width and height except 768, which limits width only. It makes only the sizes smaller than the upload, so the 1800x1200 version got five copies and no 2048. Four of those are the widths we measured encoders at in our post on [how many KB a website image should be](https://www.iminify.com/blog/3-how-many-kb-should-a-website-image-be-measured-at-four-widths#what-ten-real-images-cost-at-four-widths). JPEG copies are written at quality 82 and WebP copies at 86 ([`class-wp-image-editor.php`](https://github.com/WordPress/wordpress-develop/blob/7.1.2/src/wp-includes/class-wp-image-editor.php)).

To see what 82 costs, we redid the resize behind each of the 24 JPEG files both paths made from the photo's two versions, the way each path does it, and saved the result losslessly. Encoded at 82, every rebuild was byte-identical to what WordPress stored. Then we scored each stored file against its lossless twin with SSIMULACRA 2: all 24 landed between 74.6 and 84.0. On the metric's own scale, [70 is "high quality" and 80 means distortion you won't notice side by side](https://www.iminify.com/blog/1-ssimulacra-2-scores-in-practice-what-50-70-and-90-cost-in-kilobytes#the-ssimulacra-2-scale-is-a-set-of-viewing-tests).

PNG copies stay PNG, and that goes badly for screenshots. Our 1440x900 desktop screenshot was 303 KB. Its 1024-pixel copy was 300 KB from the server and 350 KB from the browser, and the four copies together outweighed the upload on both paths. Shrinking a screenshot mixes neighbouring pixels into new shades: the original has 27,474 colours, the server's 1024 copy 67,221 and the browser's 73,688, and a lossless format has to store every one.

## Since 7.1, the upload button decides who compresses the copies

WordPress 7.1 added [client-side media processing](https://make.wordpress.org/core/2026/07/22/client-side-media-processing-in-wordpress-7-1/): in the block editor, the browser resizes and encodes the copies with libvips compiled to WebAssembly, then uploads them one by one. It needs Chrome or Edge 137 or later (146 on Android) and a site served over HTTPS (or `localhost`), and it steps aside on low-memory or single-core devices, slow or Save-Data connections, and sites whose Content Security Policy blocks `blob:` workers. Firefox and Safari fall back too.

For the 6000 px version, the Image block's Upload button sent the original, then seven `/sideload` requests and a `/finalize`. With the 1800 px version, Media > Add Media File and the "Upload files" tab in the same block's Media Library dialog went to `async-upload.php`, and their copies matched the server path to the byte.

The same files, both ways:

| Upload | Files WordPress made | Server path | Browser path |
|---|---|---|---|
| Fjord photo, 1800x1200 JPEG, 832 KB | 5 copies | 691 KB | 640 KB |
| The same photo, 6000x4000 JPEG, 5.0 MB | 6 copies + `-scaled` | 1,942 KB | 2,193 KB |
| Desktop screenshot, 1440x900 PNG, 303 KB | 4 copies | 553 KB | 651 KB |

The 1800 px version is a 4:4:4 JPEG, with colour at full resolution. On the server its copies kept that; the browser wrote 4:2:0, which [libvips](https://github.com/libvips/libvips/blob/v8.18.3/libvips/foreign/vips2jpeg.c) uses below quality 90. That makes the server's copies of this version heavier (encoded at 4:2:0 instead, its 1024 copy came out 20 percent smaller), and they scored higher: 82.1 to 84.0, against 76.4 to 78.4 for the browser's.

The 6000 px download was already 4:2:0, both paths wrote 4:2:0, and the browser's copies came out 13 percent heavier. To find out why, we encoded both paths' lossless twins with one encoder at identical settings: the browser's pixels cost 9 to 14 percent more on every size of both versions. The difference is in the resize. WordPress's server code resizes with a triangle filter and then sharpens, after a quick pixel-sampling step to five times the target size whenever the copy has under a ninth of the original's area ([`class-wp-image-editor-imagick.php`](https://github.com/WordPress/wordpress-develop/blob/7.1.2/src/wp-includes/class-wp-image-editor-imagick.php)). libvips shrinks in the JPEG decoder where it can and finishes with [lanczos3](https://github.com/libvips/libvips/blob/v8.18.3/libvips/resample/resize.c).

WordPress's [dev note](https://make.wordpress.org/core/2026/07/22/client-side-media-processing-in-wordpress-7-1/) says libvips's JPEGs are "reduced ~15% with MozJPEG like encoding". On the 6000 px version we measured the opposite. WordPress's browser code passes libvips a quality of 82 and a list of metadata to keep, nothing else ([`packages/vips/src/index.ts`](https://github.com/WordPress/gutenberg/blob/dd294ab86ceaf9f3d7d42a09b78f399238ce8826/packages/vips/src/index.ts)), and the [wasm-vips build](https://github.com/kleisauke/wasm-vips/blob/v0.0.18/build.sh) WordPress ships compiles MozJPEG with its extra compression switched off by default.

## A retina laptop can download your untouched original

We put both server-path versions in a post at the block editor's default size, Large, on Twenty Twenty-Five, and opened it in Chrome at three emulated screen sizes:

| Screen | 1800 px upload | 6000 px upload |
|---|---|---|
| Laptop, 1440 px wide, 1x | 1024 copy, 181 KB | 1024 copy, 149 KB |
| Phone, 390 px wide, 3x | 1536 copy, 377 KB | 1536 copy, 316 KB |
| Laptop, 1440 px wide, 2x | **your original, 832 KB** | 2048 copy, 542 KB |

WordPress lists the copies in the image's `srcset` (all but the cropped thumbnail), plus the full size when it's 2,048 pixels wide or less, and tells the browser the image is up to 1,024 pixels wide. A 2x screen asks for 2,048 pixels, so it takes the widest file on offer. For the 1800 px version, that's the file you uploaded, untouched. For the 6000 px one, the full size is the 2560 px `-scaled` file, which is too wide for the list, so the 2048 copy wins.

That holds for the first three images, and a featured or header image shown first counts toward those three. From the fourth, WordPress lazy-loads the image and adds `sizes="auto"`, so the browser can use the width the image is shown at (645 pixels here). In a second test post with the 1800 px version fourth, the 2x laptop got its 1536 copy, 377 KB.

## What to do before you upload

Compress the original yourself, because WordPress won't, and on the 2x laptop above it was the file the page sent for the 1800 px version. Use [our JPEG compressor](https://www.iminify.com/compress-jpeg) or any other; WordPress makes its copies from whatever you send, at 82, either way.

For screenshots, upload a palette PNG. We uploaded the Smart output of [our PNG compressor](https://www.iminify.com/compress-png) for the same screenshot, 87 KB with 230 colours, from our published showcase of 20 September 2026. Both paths kept every copy as a palette PNG, and the 1024 copy weighed 124 KB from the server and 133 KB from the browser, against 300 and 350 KB from the full-colour upload.

Two filters change the rest. Put them in a file in `wp-content/mu-plugins/`, creating the folder if it isn't there:

```php
<?php
// wp-content/mu-plugins/image-settings.php
add_filter( 'wp_editor_set_quality', fn() => 90 );
add_filter( 'wp_client_side_media_processing_enabled', '__return_false' );
```

The first filter sets the quality of every new copy, and on our test site both paths wrote 90. The five copies of the 1800 px version grew 40 percent on the server and 69 percent in the browser, where 90 is also where libvips stops subsampling colour. We'd raise it only for copies that look soft to you. The second sends the Image block's uploads back to the server, so an author's browser no longer decides which copies you get. Neither touches images already in the library.
