Original research
Can a PDF fit an upload limit?
Five kinds of document, five common upload limits, measured with the loop behind Compress to 500 KB, 1 MB and 2 MB.
What this measured
At 500 KB and above every document fitted, and only the 18-page scan needed its pages turned into pictures to get there. At 200 KB the scan could not fit at all, and at 100 KB three of the five could not: a document that is mostly photographs runs out of room before it runs out of settings. Every document with text kept every word at every limit.
What this does and does not establish
- The corpus is synthetic — the same five generated documents as the compression benchmark, rebuilt byte for byte from a seeded script, so anyone can check the figures. Real documents vary more.
- Each case was run once. The sizes are deterministic; the times are indicative, from one server-class machine. A phone will be slower.
- A limit here means the decimal figure. "1 MB" is measured as 1,000,000 bytes, the stricter of the two ways upload forms count, because that is what the tool aims for.
- "Fits" is not "readable". The pixel difference column says how far page one moved from the original; whether that is acceptable depends on the document.
Method
Each document was handed to the same compress-target-run.js that runs on the Compress to 500 KB, 1 MB and 2 MB pages, in a real browser, on the real page. The loop tries the gentlest settings first: it recompresses the images at falling resolution and quality, and only if that is not enough does it turn the pages into images. It stops at the first result that fits; if none fits it keeps the smallest. Every output was then checked with tools that are not the ones under test: pdftotext for the words, pdftoppm for the page-one pixel difference, and qpdf --check for structure. Every output passed the structure check.
| Run date | November 1, 2026 |
|---|---|
| Site version | 3.37.0 |
| Engine | assets/js/compress-target-run.js |
| Libraries | pdf-lib 1.17.1, PDF.js 3.11.174 |
| Browser | Chromium (HeadlessChrome/141.0.0.0), headless, Linux x86-64 |
| Timing | one run per case, in-page wall clock |
| Size limits measured |
100 KB — fits under 100,000 bytes 200 KB — fits under 200,000 bytes 500 KB — fits under 500,000 bytes 1 MB — fits under 1,000,000 bytes 2 MB — fits under 2,000,000 bytes |
Results
Each cell is one run: whether the document fitted, the size it came out at, how it got there, the time taken, and Δ, the mean difference per colour channel on page one (0–255; under 1 is invisible, above 10 is visibly softer). "Images recompressed" keeps the text, links and form fields; "pages as images" keeps only a picture of each page.
| Document | 100 KB | 200 KB | 500 KB | 1 MB | 2 MB |
|---|---|---|---|---|---|
| Text-heavy document 42 · 93 KB |
Fits 93 KB images recompressed · 0.0 s · Δ 0 |
Fits 93 KB images recompressed · 0.0 s · Δ 0 |
Fits 93 KB images recompressed · 0.0 s · Δ 0 |
Fits 93 KB images recompressed · 0.0 s · Δ 0 |
Fits 93 KB images recompressed · 0.0 s · Δ 0 |
| Scanned document 18 · 10.52 MB |
Does not fit — closest 416 KB pages as images · 30.7 s · Δ 11.42 |
Does not fit — closest 416 KB pages as images · 31.3 s · Δ 11.42 |
Fits 416 KB pages as images · 30.2 s · Δ 11.42 |
Fits 778 KB images recompressed · 4.7 s · Δ 10.66 |
Fits 1.89 MB images recompressed · 2.7 s · Δ 4.5 |
| Photo-heavy brochure 12 · 4.12 MB |
Does not fit — closest 106 KB images recompressed · 17.0 s · Δ 1.36 |
Fits 139 KB images recompressed · 1.7 s · Δ 1.07 |
Fits 373 KB images recompressed · 0.7 s · Δ 0.47 |
Fits 373 KB images recompressed · 0.6 s · Δ 0.47 |
Fits 373 KB images recompressed · 0.6 s · Δ 0.47 |
| Presentation export 24 · 2.87 MB |
Fits 90 KB images recompressed · 1.8 s · Δ 1.16 |
Fits 172 KB images recompressed · 1.0 s · Δ 0.65 |
Fits 335 KB images recompressed · 0.6 s · Δ 0.35 |
Fits 335 KB images recompressed · 0.5 s · Δ 0.35 |
Fits 335 KB images recompressed · 0.5 s · Δ 0.35 |
| Mixed text and images 30 · 3.80 MB |
Does not fit — closest 135 KB images recompressed · 20.8 s · Δ 0.77 |
Fits 168 KB images recompressed · 1.8 s · Δ 0.54 |
Fits 422 KB images recompressed · 0.6 s · Δ 0.27 |
Fits 422 KB images recompressed · 0.7 s · Δ 0.27 |
Fits 422 KB images recompressed · 0.7 s · Δ 0.27 |
Observations
A scanned page has a floor of about 23 KB. The 18-page scan at its most aggressive setting came out at 416 KB, about 23 KB a page. That is why it fitted 500 KB and nothing smaller: below that point there is no setting left, only an unreadable page. For a scan, divide the limit by the pages before trying.
Most documents reach 1–2 MB on the gentlest pass, in under a second. The brochure, the presentation and the report all fitted 500 KB, 1 MB and 2 MB on the first and gentlest attempt, in 0.5 to 0.8 seconds, with page one visibly unchanged (Δ under 0.5).
Halving the limit doubled the visible cost for the scan. At 2 MB its page one moved by Δ 4.5; at 1 MB by 10.7; at 500 KB, with its pages turned into images, by 11.4. The file got smaller each time; the page got softer each time.
When a limit is out of reach, the search is long. The runs that could not fit took 17 to 31 seconds, because every setting was tried before giving up. The brochure's closest result at 100 KB was 106 KB, 8% over; the report's was 135 KB. Splitting the document would have been faster and better.
Text was never the problem. The 42-page text-only agreement is 93 KB and fitted every limit untouched, and every document with a text layer kept every one of its words.
Reproducing this
Everything ships with the site under tools/: benchmark-corpus.py builds the documents, benchmark-run.mjs runs them through the live tool page of a running copy of the site, benchmark-verify.py runs the independent checks and benchmark-publish.py writes config/size-target-study.php, which is all this page reads. No figure here is typed by hand.




