Skip to content
pageinspection

Text compression, and the responses that miss out

Text compresses by roughly three quarters and switching it on is one line. Which is why the interesting question is not whether you have it, but which of your responses are quietly going out uncompressed anyway.

The short answer

Text compresses by roughly 70 to 90 percent, so HTML, CSS, JavaScript, JSON and SVG should never be served uncompressed. Brotli beats gzip by a further 15 to 20 percent on the same content. Images and video are already compressed and gain nothing, which is why the assets people miss are usually JSON APIs and SVG.

ImportantAudit check · Compression

What compresses, and by how much

Compression works on repetition, so anything with structure gains enormously and anything already compressed gains nothing. The rough figures are stable enough to plan with.

ratios.txt
type                     typical saving   worth compressing?
-----------------------  ---------------  -----------------------------
HTML                     70 to 80%        yes, always
CSS                      75 to 85%        yes, always
JavaScript               65 to 75%        yes, always
JSON and API responses   80 to 90%        yes, and often forgotten
SVG                      60 to 70%        yes, and usually missed
plain text, XML          70 to 80%        yes, sitemaps included
JPEG, PNG, WebP, AVIF    0 to 2%          no. already compressed
MP4, WOFF2               0 to 1%          no. wastes CPU on both ends
PDF                      0 to 10%         usually not worth it

Two rows are the ones this check tends to find. SVG is text and it is served as an image, so asset pipelines that decide what to compress by looking at the type prefix skip it. And JSON from an API is frequently served by a different layer to the one compressing your pages, so a site whose HTML is compressed can be shipping uncompressed payloads that are larger than the document.

On the algorithms: gzip is universal and fine. brotli saves a further 15 to 20 percent on text and is supported everywhere that matters. Both are negotiated by the client through the Accept-Encoding request header, so serving both and letting the browser choose is the normal arrangement rather than a decision.

Where compression goes missing

Almost nobody has compression switched off entirely. What sites have is a gap, and the gaps are predictable.

  • A type list that has not been updated. The server compresses a named set of content types, and anything outside the list is served raw. The list was written before application/json, image/svg+xml and application/manifest+json mattered.
  • A minimum size threshold set too high. Reasonable in principle, since compressing 200 bytes costs more than it saves. Set at 20KB it excludes most of your CSS and every API response.
  • A proxy that strips the header. The origin compresses, something in the middle decompresses to inspect and forgets to recompress. This is the hardest one to diagnose because the configuration you are looking at is correct.
  • Assets on a different host. Your pages come from a CDN with compression on, your uploads come from object storage with nothing configured, and the second is where the volume is.
  • Responses that opt out. Streamed responses, server-sent events and anything that sets its own encoding. Sometimes deliberate, and worth knowing which of yours are.

The pattern to notice: every one of these produces a site where the home page is compressed and something else is not. Testing one URL and concluding the site is fine is the mistake this check exists to correct.

Two requests, and the difference between them

Measuring this means asking for the same URL twice: once declaring that you accept compressed responses, and once not, then comparing the sizes that come back. A single fetch gives one of those two numbers, and one number cannot express a ratio.

This one needs the full crawl

Measuring this means requesting the same URL twice, once declaring support for compression and once not, then comparing the sizes that come back.

The instant search on this site audits a single page, so rather than show you a verdict it cannot support, this guide sends you to the place the check actually runs.

The two-request version is in the last section of this guide, and it is short enough to run against a handful of your own URLs right now.

Why a setting nobody argues about is Important

Not because the absence is catastrophic. An uncompressed site works: pages load, crawlers fetch them, nothing is excluded, which keeps it clear of the Critical band.

It earns Important on the size of the effect against the size of the change. Cutting the transfer weight of every text response by three quarters is one of the largest performance improvements available anywhere, and the change is a configuration line with no code, no content and no judgement. Nothing else in this audit has that ratio.

The rating also accounts for who pays when it is missing. Compression matters most on a slow connection and a cheap device, which is to say it matters most for the visitors least able to absorb the cost. A finding that is free to fix and lands hardest on the people with the least bandwidth is not a refinement.

Measuring the ratio on your own responses

One function, run against the URLs that carry your weight: a page, a stylesheet, a bundle, an SVG, an API response.

check-compression.sh
ratio() {
  raw=$(curl -s -o /dev/null -H 'Accept-Encoding: identity' -w '%{size_download}' "$1")
  br=$(curl -s -o /dev/null -H 'Accept-Encoding: br, gzip' -w '%{size_download}' "$1")
  enc=$(curl -s -I -H 'Accept-Encoding: br, gzip' "$1" \
    | grep -i '^content-encoding:' | cut -d' ' -f2 | tr -d '\r')

  if [ "$raw" -gt 0 ] 2>/dev/null; then
    printf '%-46s %7s -> %7s  %3s%%  %s\n' \
      "$1" "$raw" "$br" "$(( 100 - br * 100 / raw ))" "${enc:-NONE}"
  fi
}

ratio https://example.com/
ratio https://example.com/assets/app.css
ratio https://example.com/assets/app.js
ratio https://example.com/icons/logo.svg
ratio https://example.com/api/products
ratio https://example.com/sitemap.xml

A saving near zero with an encoding of NONE is an uncompressed response. Read the list rather than the first row: the interesting output is almost always the SVG or the API endpoint rather than the page, because the page is the one somebody checked when they configured this.

Then look at what compression cannot help with.

On most pages the images are the weight, and they are already compressed. Once your text responses are gzipped, the remaining performance work is image formats, image dimensions and how much JavaScript you ship, and no amount of encoding touches any of those.

The neighbouring check on caching is the other half of this one, and it is worth more on a repeat visit than compression is: a cached asset transfers nothing at all, at any compression ratio.

Questions this check raises

Should I use gzip or brotli?
Both, negotiated by the client. Brotli compresses text 15 to 20 percent better than gzip at comparable settings and is supported by every current browser; gzip is the fallback for anything that does not advertise brotli. Every serious CDN does this automatically.
Which files should not be compressed?
Anything already compressed: JPEG, PNG, WebP, AVIF, MP4, and most font formats. Recompressing them costs CPU and gains a fraction of a percent, and in some cases makes the file marginally larger.
Where does compression usually go missing?
On the responses nobody looks at. HTML and CSS are almost always covered because they are what the default configuration lists. JSON API responses, SVG served from an assets path, and files coming from a second origin or a legacy server routinely are not.