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 dateNovember 1, 2026
Site version3.37.0
Engineassets/js/compress-target-run.js
Librariespdf-lib 1.17.1, PDF.js 3.11.174
BrowserChromium (HeadlessChrome/141.0.0.0), headless, Linux x86-64
Timingone 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.

Tools this article covers