A form says maximum 500 KB. Your file is 500 KB — your computer says so. The upload is rejected anyway. Nothing is broken, nobody made a mistake, and the file really is 500 KB. It is just that at least three different programs counted it, and at least two of them used a different kilobyte.
This is usually explained and left there: KB is 1,000 bytes, KiB is 1,024, some people mix them up. That is true and not very useful, because it does not tell you which one is on the other end of the form you are staring at. So we measured it — our own compressors, the defaults that ship with three widely used web frameworks, and our own interface, which turned out to be part of the problem.
The gap has a size, and it is not small
Two readings of the same limit:
| Stated limit | If KB means 1,000 | If KB means 1,024 | Files caught in between |
|---|---|---|---|
| 100 KB | 100,000 bytes | 102,400 bytes | 2,400 bytes wide |
| 500 KB | 500,000 bytes | 512,000 bytes | 12,000 bytes wide |
| 1 MB | 1,000,000 bytes | 1,048,576 bytes | 48,576 bytes wide |
| 2 MB | 2,000,000 bytes | 2,097,152 bytes | 97,152 bytes wide |
The last column is the trap. A 2 MB file is not rejected because it is wildly too big; it is rejected because it landed in a 97 KB window where one program thinks it fits and another does not. And the closer you compress to the stated number, the more likely you are to land exactly there. Compressing harder is the fix, which feels absurd when the tool already told you it hit the target.
What our compressors actually produce
We ran the same difficult source image — 13.5 MB, noisy, the kind that resists compression — through four target-size pages in a real browser and read the byte count of each downloaded file.
| Page | Target you type | Bytes produced | Under 1,000-based limit? | Under 1,024-based limit? |
|---|---|---|---|---|
| compress-image-to-100kb | 100 KB | 79,141 | Yes | Yes |
| compress-image-to-200kb | 200 KB | 189,136 | Yes | Yes |
| compress-image-to-500kb | 500 KB | 483,139 | Yes | Yes |
| compress-image-to-1mb | 1,000 KB | 975,390 | Yes | Yes |
Every output clears the stricter of the two limits. That is deliberate: these pages treat N KB as N × 1,000 bytes, the smaller number, so that a file which passes here also passes a form counting the other way. It was not always so. Until 17 September 2026 the presets used 1,024, which meant a page called “compress to 100 KB” could hand you a 101,000-byte file — correct by its own arithmetic, rejected by a form checking for 100,000.
The part we got wrong
While measuring for this article we found the same confusion living inside our own interface. The compressors were targeting 1,000-byte kilobytes, but the function that writes the result on screen was still dividing by 1,024. So the tool did its job and then misreported it:
| Target you typed | Bytes you got | What the page said (before) | What the page says now |
|---|---|---|---|
| 100 KB | 79,141 | 77.3 KB | 79.1 KB |
| 500 KB | 483,139 | 471.8 KB | 483.1 KB |
| 1 MB | 975,390 | 0.93 MB | 0.98 MB |
Neither column is a lie — 483,139 bytes genuinely is 471.8 KiB — but asking for 500 and being shown 471.8 makes it look as though the tool overshot its aim and wasted quality it did not need to spend. It had not. It was answering in a different unit than the question was asked in. As of today every size on this site is written in the same 1,000-byte kilobyte the compressors target, so the number you type, the number you read and the number the upload form counts are finally the same number.
Which kilobyte is on the other end?
You usually cannot tell from the form. But the defaults underneath it are documented, and they lean one way.
| Where the limit lives | What the documentation says | In bytes |
|---|---|---|
PHP upload_max_filesize | “1M equals one Megabyte or 1048576 bytes. 1K equals one Kilobyte or 1024 bytes.” | 1,048,576 per “MB” |
Django DATA_UPLOAD_MAX_MEMORY_SIZE | Default 2621440, described in the docs as “2.5 MB” | 2,621,440 — which is 2.62 MB decimal |
nginx client_max_body_size | Default 1m; the documentation never states the multiplier | Not documented |
Read those rows together and the picture is clear enough. PHP does not merely use 1,024 —
its manual stops to acknowledge the conflict, noting that what it calls a kilobyte is what
the IEC standard calls a kibibyte. Django's default is a binary number wearing a decimal
label: 2,621,440 bytes is exactly 2.5 × 1,048,576, yet the documentation calls it
2.5 MB, which in decimal it is not. And nginx, the most common thing standing in front of
all of them, ships a default of 1m without ever defining what m
multiplies by.
This is the useful finding: the systems checking your upload almost always count in 1,024s, while the label on the form, the number in the email from IT and the size your file manager shows may be counting in anything. You are not being told which. So the safe assumption is the one that survives both.
Your own computer is a third opinion
Before the file ever reaches a server, two programs on your machine have already disagreed about it. Windows File Explorer divides by 1,024 and writes the result as “KB”, so our 483,139-byte file appears there as 472 KB. macOS Finder has divided by 1,000 since OS X 10.6 and shows the same file as 483 KB. Neither is wrong, neither says which convention it used, and the two numbers differ by more than the width of some upload windows.
A browser, usefully, does not round at all. The size property of a selected
file is the raw byte count, which is why every tool on this site can show you an exact
figure: drop a file into any of them and the number you see is the number the server will
count, not a rounded translation of it.
What to actually do
- Target the decimal number, then take 5–10% off. For a stated 500 KB limit, aim at 450–475 KB rather than 500. This clears both readings and leaves room for the second thing that changes file size: some systems re-encode or re-wrap what you upload.
- Never compress to exactly the limit. Hitting 499,980 bytes against a 500,000-byte check is a coin flip once rounding in a progress bar, a proxy or a form validator gets involved. The target-size compressor lets you type any number, so type the safe one.
- Read the byte count, not the label. If a form rejects a file your computer calls the right size, the byte count will usually explain it immediately.
- If the file is already small enough, compressing again does nothing. Our compressors say so rather than re-encoding for no reason — which has a consequence worth knowing about if you were also hoping to strip the photo's metadata: see what actually removes a photo's location.
- For PDFs the same arithmetic applies but the levers are different; where PDF file size comes from covers which part of the document is actually costing you the bytes, and the PDF compressor lets you choose it.
The short version
There are two kilobytes in common use, they differ by 2.4%, and that is comfortably wider than the margin most people leave when compressing to a limit. The software checking your upload probably counts in 1,024s; the label on the form probably does not say so. Compress to the smaller interpretation with a little room to spare and the question stops mattering, which is the only reliable way to win an argument between two definitions.
How this was measured
The four compression figures come from driving the live pages in headless Chrome through the DevTools protocol with the same 13.5 MB source image, capturing each download and reading its length in bytes — not the size reported in the interface, which is the very thing this article is about. The source is generated noise rather than a fixed file, so a repeat run lands a few hundred bytes either side of the numbers above; what does not move is which side of each limit the output falls on. The framework figures are quoted from the current PHP, Django and nginx documentation rather than from a running server, so they describe the shipped defaults; any given site may have changed them. The Windows and macOS figures are the documented rounding behaviour of each file manager applied to our measured byte count.