# Brotli Was Bigger Than Gzip on 31% of WordPress Sites in 2026: Read the Bytes, Not the Header

> 2,787 scan reports and 159 byte-identical documents, measured 29 September 2026. Brotli beat gzip by a median 1.29 KB, and lost outright on 31.4% of sites.

- Published: 2026-09-29
- Updated: 2026-10-04
- Author: xSpeed Cache Team
- Tags: Compression, Performance, Research, TTFB, WordPress, check:D3, platform:wordpress
- Canonical: https://xspeedcache.com/blog/wordpress-compression-gap-study/

---

Updated September 2026

We harvested 2,787 scan reports from our own archive on 29 September 2026, 1,826 of them WordPress, and 98.3% of those already compress their HTML. The interesting number is the other one: on 159 documents we fetched twice and decompressed to prove they were byte-identical, the brotli the server produced was **larger than its own gzip on 50 of them**, 31.4%.

The comparison people expect here is compressed against uncompressed, and on WordPress that question is close to settled. The more useful comparison is gzip against brotli on the same document, because that is the upgrade every performance tool recommends, and the bytes say it is worth a median 1.29 KB. This study covers the HTML document only, from one probe location, on sites that submitted themselves to a public scanner.

## Quick Summary: What Your Content-Encoding Header Is Worth

| If you want… | Do this | Why |
|:---|:---|:---|
| To know whether compression is your problem | Check whether the header exists at all, then stop | 98.3% of WordPress sites here compress; only 31 of 1,826 do not |
| To know what gzip-to-brotli buys | Fetch the page twice and compare wire bytes | Median gain on 159 verified documents is 1.29 KB, negative on 31.4% |
| The compression win that is large | Get any compression onto an uncompressed site | Median document: 226.7 KB raw, 42.3 KB compressed, an 81.7% cut |
| To judge a site from a scan score | Read the bytes, not the encoding name | Our rubric gives full marks for a `br` header even when that brotli is bigger than the site's gzip |

## How We Measured This, and the Six Things It Cannot Tell You

The method and its limits come first, so a reader can judge each number.

**The population.** 2,787 scan reports on 2,787 distinct hostnames, taken from our own report index on 29 September 2026 with zero fetch errors, covering scans run between 30 August and 29 September 2026. 1,826 are WordPress. Every report carries a server probe measured from Vilnius, Lithuania; 1,046 also carry a mobile Lighthouse run and 372 carry Chrome User Experience Report field data.

**The recompression probe.** The archive records which encoding a server chose, not what the alternatives would have cost. So we sampled 391 WordPress hosts and requested each homepage four times, with `Accept-Encoding` set to `identity`, `gzip`, `br` and the browser-realistic `gzip, br, deflate`, recording the wire bytes and the `Content-Encoding` returned each time. 312 answered 200 to the identity request; 59 returned 403 to our agent, 18 failed to connect and 2 returned 503.

**The validity gate that changed the result.** A first pass compared gzip bytes against brotli bytes directly and produced differences too large to be a compression-level effect. A WordPress homepage can differ between two requests, so we refetched every host that served both encodings on demand, decompressed both bodies, and kept only hosts whose decompressed lengths matched to the byte. 159 of 168 matched, 94.6%. The nine that did not are excluded from every brotli-against-gzip figure below.

Six things this cannot tell you:

- 🚫 **Subresources are out of scope.** CSS, JavaScript and fonts are compressed separately and are usually the larger share of transfer weight.
- 🚫 **One location, one moment.** Every probe ran from a single region within one hour, so a server varying by region would not show it.
- 🚫 **Self-selected sites.** Somebody ran each of these through a public scanner, skewing toward owners who already care about performance.
- 🚫 **403 attrition is not random.** The 59 hosts that refused our agent are likelier to sit behind a strict WAF, which correlates with well-configured platforms.
- 🚫 **No causal claim about speed.** Compression changes bytes on the wire. A timing difference reported beside a compression state is a correlation with the whole server stack.
- 🚫 **Compression level is inferred, never read.** No server reports its brotli quality setting, so we measure the output.

## What WordPress Actually Serves

The uncompressed-HTML problem this study set out to investigate turns out to be small, and saying so is more useful than inflating it.

| HTML compression | WordPress sites | Share | Rest of the archive | Share |
|:---|---:|---:|---:|---:|
| Brotli (`br`) | 1,299 | 71.1% | 664 | 69.1% |
| Gzip | 496 | 27.2% | 235 | 24.5% |
| None | 31 | 1.7% | 62 | 6.5% |
| **Total** | **1,826** | **100%** | **961** | **100%** |

WordPress compresses HTML more reliably than the rest of the archive, and by a factor of nearly four on the failure case. That is worth stating plainly because the expectation runs the other way: a default install inherits compression from the web server, and mainstream hosts turn it on.

For dated contrast, the HTTP Archive's [2021 Web Almanac compression chapter](https://almanac.httparchive.org/en/2021/compression) reports that `text/plain` and `text/html` were "the only content types that are compressed less than 50% of the time" in its crawl. Our 98.3% is not like-for-like: the Almanac counts every `text/html` response including third-party frames and bodies too small to compress, and five years is a long time in server defaults.

### The 31 Sites That Send Nothing

31 WordPress sites returned no `Content-Encoding` on their homepage. We reprobed every one. Of the 28 that answered, 23 still refuse all compression, and 21 cleared the eligibility gate for the byte ledger. Their median raw HTML document is 155.8 KB, running from 57 KB to 456 KB.

Those are the sites where compression is a real finding: that document compresses to roughly a fifth, and the fix is one directive in a server configuration file. Five of the 28 did answer gzip when asked properly, meaning either the site was fixed after its scan, or its configuration varies in a way one request cannot see.

## The Compression Ledger

This is the framework, simple enough to run on your own site in three commands. Fetch the same URL three times with different `Accept-Encoding` values, record the wire bytes, and decompress to confirm the document matched. The gap between rows two and three is the only number the gzip-to-brotli decision rests on.

```bash
# Row 1: the document uncompressed
curl -s -o /dev/null -H 'Accept-Encoding: identity' \
  -w 'identity %{size_download}\n' https://example.com/

# Row 2: what gzip charges
curl -s -o /dev/null -H 'Accept-Encoding: gzip' \
  -w 'gzip     %{size_download}\n' https://example.com/

# Row 3: what brotli charges
curl -s -o /dev/null -H 'Accept-Encoding: br' \
  -w 'br       %{size_download}\n' https://example.com/
```

Two traps, both of which we hit. Without `--compressed`, curl reports bytes as they arrived, which is what you want; adding that flag makes every row report the decompressed size and the comparison silently becomes meaningless. And a server that ignores `Accept-Encoding: identity` hands you a compressed body with no warning, so confirm row one carries no `Content-Encoding` header. Three of our 391 hosts failed that check.

Run against 293 eligible hosts, the ledger looks like this. Every figure is a median.

| Ledger row | Bytes | Against the row above |
|:---|---:|:---|
| Raw HTML document (`identity`) | 226.7 KB | — |
| Compressed with gzip | 42.3 KB | 184.3 KB saved, 81.7% |
| Compressed with brotli | 42.3 KB | 1.29 KB saved, 3.11% |

The first step is worth 143 times the second. That ratio holds whichever way we cut the sample.

![A three-row compression ledger showing a 226.7 KB raw HTML document falling to 42.3 KB with gzip and 42.3 KB with brotli, the first step labelled 184.3 KB saved and the second 1.29 KB](https://xspeedcache.com/images/blog/wordpress-compression-ledger.webp)

## Brotli Against Gzip on 159 Byte-Identical Documents

This is the part we did not expect, and checked hardest.

| Measure | Value |
|:---|---:|
| Documents compared, decompressed lengths identical | 159 |
| Median brotli saving over gzip | 1.29 KB (3.11%) |
| Mean brotli saving over gzip | 0.69 KB (0.4%) |
| Brotli **larger** than the same server's gzip | 50 of 159 (31.4%) |
| Brotli saved under 2 KB | 90 of 159 (56.6%) |
| Median ratio, gzip against brotli | 5.47× against 5.55× |

The two algorithms reach almost the same ratio on real WordPress HTML. Brotli's advantage in the specification is real. [RFC 7932](https://www.rfc-editor.org/rfc/rfc7932.html), published by the Internet Engineering Task Force in July 2016, describes a format that compresses "considerably better than the gzip program", and Chrome's [Lighthouse text-compression documentation](https://developer.chrome.com/docs/lighthouse/performance/uses-text-compression) states that "if the browser supports Brotli (`br`) you should use Brotli because it can reduce the file size of the resources more than the other compression algorithms."

Both statements are about the algorithms. Neither is about what a web server does at request time, and that is where the gap opens.

### Why a Server's Brotli Loses to Its Own Gzip

Brotli has eleven quality levels. At the highest it beats gzip comfortably; at the lowest it does not. Compressing a page on the fly costs CPU on every uncached request, so servers and proxies ship dynamic brotli at a low level and precompressed static brotli at a high one. A WordPress homepage assembled by PHP is dynamic, so it gets the cheap setting.

The Almanac chapter puts it from the other direction: "For dynamic compression, we have to make sure that the user doesn't have to wait longer for a more heavily compressed file." A server tuned that way is behaving correctly. The scoring on top of it is wrong.

The four largest offenders in our sample, all verified byte-identical:

| Host | Document | Gzip | Brotli | Brotli cost |
|:---|---:|---:|---:|---:|
| `joss.pe` | 976.1 KB | 126.8 KB | 231.8 KB | +83% |
| `pray-for-souls.com` | 187.1 KB | 33.4 KB | 54.2 KB | +62% |
| `indianassociationdenmark.com` | 407.5 KB | 65.7 KB | 103.3 KB | +57% |
| `whitecloudbd.com` | 820.4 KB | 112.5 KB | 165.5 KB | +47% |

`joss.pe` serves a 976 KB HTML document. Asked for gzip it sends 126.8 KB; asked for brotli it sends 231.8 KB of the same document. A browser advertising both gets the brotli, because servers prefer it when offered, so the modern algorithm costs that site 105 KB on every uncached page view.

![Scatter plot of brotli wire bytes against gzip wire bytes for 159 WordPress documents, with 50 points above the break-even diagonal where brotli is larger](https://xspeedcache.com/images/blog/brotli-vs-gzip-scatter.webp)

### Cloudflare Is the Weakest Case, Not the Strongest

Splitting the 159 by whether a CDN answered produces the result that matters most for the web's largest CDN.

| Cohort | n | Median brotli saving | Brotli larger than gzip |
|:---|---:|---:|---:|
| Behind Cloudflare | 52 | 1.22% | 19 (36.5%) |
| No CDN detected | 107 | 6.78% | 31 (29.0%) |

Cloudflare's [own compression documentation](https://developers.cloudflare.com/speed/optimization/content/compression/) explains why its sites are on brotli at all: it "supports Gzip, Brotli, and Zstandard compression when delivering content to website visitors" and applies it to `text/html`, independently of what the origin sent. That is a proxy recompressing at its own chosen level for its own cost profile, at the scale of a large share of the web. It earns the site a `br` header, a median 1.22% over gzip, and a worse result than gzip a third of the time.

This also explains the adoption pattern. 98.8% of the 582 Cloudflare WordPress sites serve brotli, against 59.4% of the 1,210 with no CDN detected. The brotli share of WordPress is substantially a measure of Cloudflare's market share, not of anything site owners configured.

## The Correlation That Is Not There

Median TTFB across the three compression states looks like a strong result and is not.

| Compression | WordPress sites | Median TTFB | Behind a CDN | Served from cache |
|:---|---:|---:|---:|---:|
| None | 31 | 483 ms | 3.2% | 38.7% |
| Gzip | 496 | 435.5 ms | 7.1% | 50.8% |
| Brotli | 1,299 | 264 ms | 44.6% | 69.4% |

A 219 ms spread invites a causal reading, and the reading is wrong for a mechanical reason: time to first byte is measured when the first byte arrives, and compression changes how many bytes follow it. Spearman's rho between compression rung and TTFB is **−0.116** across all 1,826 WordPress sites, and **−0.100** with CDN-backed sites removed. The large median gaps are cohort composition: the brotli group is 44.6% CDN-backed and 69.4% cache-hit, the gzip group 7.1% and 50.8%.

Narrowing to no CDN and a cache hit still leaves gzip at 346 ms against brotli at 197 ms on 231 and 463 sites. We are not publishing that as a compression effect either. Brotli support marks a recently configured server, and such a server is faster for reasons unrelated to which compressor it loaded. A marker is not a mechanism, and this dataset cannot separate them. Our study of [353 WordPress sites in lab against field data](https://xspeedcache.com/blog/pass-lighthouse-fail-core-web-vitals/) is the method to copy for the honest version.

## What Our Own Rubric Gets Wrong

Every scan report carries a delivery check, D3, named "HTML compression (Brotli/GZIP)" and worth 5 of 100 points. Across all 2,787 reports its behaviour is deterministic, with no exceptions:

| Encoding | D3 status | Points earned | Reports |
|:---|:---:|---:|---:|
| `br` | ✅ pass | 5 of 5 | 1,963 |
| `gzip` | ⚠️ partial | 2.5 of 5 | 731 |
| none | ❌ fail | 0 of 5 | 92 |
| `identity` | ❌ fail | 0 of 5 | 1 |

D3 reads the header, not the bytes. Three consequences follow, and we own all three because we built the scanner.

- 🟡 **We dock 496 WordPress sites 2.5 points each, 1,240 in total, for a difference worth a median 1.29 KB.** On 56.6% of them that penalty attaches to a saving under 2 KB.
- 🔴 **We award full marks to brotli that is worse than the site's own gzip.** `joss.pe` scores 5 of 5 on D3 while shipping 105 KB more than it needs to. A check that cannot tell those apart is measuring a string, not a page.
- 🟢 **The 31 sites the check is right about take the same maximum penalty a misconfigured brotli never takes.** A 155.8 KB document served raw and a 1.29 KB brotli shortfall are a hundredfold apart in cost and under two points apart in our scoring.

Google's tooling draws the line differently, and better. That same Lighthouse page states the audit gathers responses that "do not include a content-encoding header set to `br`, `gzip`, or `deflate`", then compresses each with GZIP to estimate savings, and that "if the original size of a response is less than 1.4KiB, or if the potential compression savings is less than 10% of the original size, then Lighthouse does not flag that response". Gzip and brotli count as equivalent passes, and as of Lighthouse 13 the audit has moved into the Document request latency insight. A site on gzip gets no signal from Google at all, and that is the right call.

We are reporting this to the team that owns the scanner rather than quietly rescoring it here. The fix is to compare bytes across encodings during the scan, four requests instead of one, and withhold the point from sites whose brotli is not smaller.

## Running This Across a Fleet of WordPress Sites

Compression is set by the web server or the proxy in front of it, not by a caching plugin, and that is the honest boundary of what we build. xSpeed Cache, which is ours and WPDeveloper's, serves cached pages as static files before PHP starts, so the `Content-Encoding` on that response is whatever nginx, Apache, LiteSpeed or Cloudflare puts there. No caching plugin's dashboard toggle changes what this study measured.

What a fleet owner can do is measure it everywhere at once. The scan is free, needs no account, and reports D3 with the encoding it observed on every site you point at it. Read the check, then run the ledger on any site whose header says `br`.

Run the free scan on your own site: [xspeedcache.com/scan/](https://xspeedcache.com/scan/)

### Where Our Own Sites Land, Both Directions

Of the 159 verified documents, 77 run xSpeed Cache and 82 do not. The xSpeed group gets a median 4.33% from brotli against 2.48%, and its brotli is worse than its gzip on 27.3% of sites against 35.4%. We are not claiming credit: xSpeed does not touch HTTP compression, so the likeliest reading is a selection effect in which hosts our users are on, and 159 sites cannot separate it from noise. Elsewhere our cohort loses. xSpeed WordPress sites adopt brotli on 67.8% of installs against 74.4% without it, and sit behind a CDN 27.2% of the time against 40.2%. [Agencies running client fleets](https://xspeedcache.com/use-cases/agencies/) see both patterns at once.

If the server layer is what you keep fighting, we recommend [xCloud](https://xcloud.host/) for it, and it is ours: xCloud and WPDeveloper are both Startise companies. A managed stack is where compression level, cache policy and PHP version stop being three separate arguments with a host.

## Common Mistakes Site Owners Make With Compression

- **Chasing brotli because a tool named it.** The tool repeats the algorithm's specification and does not measure your server's configuration, which is what decides the bytes.
- **Comparing wire bytes without checking the document matched.** Nine of our 168 candidate hosts served a different homepage between two requests.
- **Reading a TTFB difference as a compression result.** Time to first byte ends before the compressed body is transferred. If compression moved that number, something else moved it.
- **Compressing already-compressed assets.** Images, video and fonts carry their own compression, which is why the Almanac notes their "representation already includes internal compression".

![Decision diagram branching on whether a Content-Encoding header exists and whether the brotli beats the gzip, with the byte threshold at each branch](https://xspeedcache.com/images/blog/compression-decision-tree.webp)

## Frequently Asked Questions

### I enabled brotli and my page got bigger. Did I break something?

Probably not. On 50 of the 159 documents we verified, the server's brotli was larger than its own gzip, and the usual cause is a low dynamic compression level chosen to keep CPU cost down. Run the ledger: if brotli loses on your site, either raise the brotli quality level for HTML or let gzip serve it.

### I ran the ledger and every row returned the same number of bytes. What went wrong?

You almost certainly used `curl --compressed`, which decompresses the body before curl counts it. Remove that flag and set `Accept-Encoding` by hand with `-H`, as in the commands above.

### My identity request came back compressed. Is the measurement still valid?

No, and you should discard that row. Some servers ignore `Accept-Encoding: identity` and compress anyway, so check for a `Content-Encoding` header before treating row one as the raw size. Three of our 391 hosts behaved this way and were excluded.

### Does a caching plugin control HTML compression?

No. Compression is applied by the web server or a proxy such as Cloudflare. A page cache decides which document is served and how fast it is assembled; the encoding on top belongs to the layer above. This is true of every caching plugin, ours included, and our [server compatibility documentation](https://xspeedcache.com/docs/server-compatibility/) covers which stacks do what.

### Is 27.2% of WordPress on gzip a problem worth fixing?

On this evidence, rarely. The median gain from moving those sites to brotli is 1.29 KB, and the change is negative on roughly three sites in ten. Google's own tooling treats gzip and brotli as equivalent passes and ignores savings under 10% of the response. Spend the effort on the document size instead.

### My scan report says D3 partial and I have gzip enabled. Is the scan wrong?

The scan reported what it observed, a gzip header rather than a brotli one, and awarded 2.5 of 5 points. The scoring is what we criticise above, and your configuration is probably fine.

### How do I check compression without a terminal?

Open your browser's developer tools, go to the Network tab, click the document request, and read the `content-encoding` line under Response Headers, the method Chrome's own documentation describes. Compare the transferred and uncompressed sizes in the same panel for the delta. Our [guide to reading a PageSpeed report](https://xspeedcache.com/blog/read-pagespeed-report-wordpress/) covers where these numbers surface in Lighthouse.

### Why does compression not improve my TTFB?

Because time to first byte is measured at the arrival of the first byte, and compression changes how many bytes come after it. Across 1,826 WordPress sites, Spearman's rho between compression state and TTFB is −0.116. If your first byte is slow the causes are in the server, and our [guide to reducing WordPress TTFB](https://xspeedcache.com/blog/reduce-ttfb-wordpress/) works through them in order.

### What about the CSS and JavaScript on the page?

Out of scope here, and usually the larger prize. We measured the HTML document because that is what our archive records a verdict for. Our study of [where page weight starts deciding mobile LCP](https://xspeedcache.com/blog/page-weight-vs-lcp-wordpress/) works on total transfer weight and finds the threshold that matters is 350 KB.

### 2,787 sites sounds small. How much should I trust these numbers?

The distribution figures rest on all 2,787 reports and are solid for this population. The brotli-against-gzip comparison rests on 159 documents and is directional: enough to show a `br` header does not guarantee fewer bytes, not enough to size the effect on your stack. That is why the framework is three commands you can run yourself.

## Conclusion: Read the Bytes, Not the Header

The compression gap on WordPress is not the one this study set out to find. Nearly every site compresses its HTML, and the 31 that do not are the only group with a large number waiting for them. The gap actually open sits one rung higher, between a `br` header and a genuinely smaller document, and roughly a third of the sites on that rung sit on the wrong side of it.

| If your goal is… | Do this | Expected gain |
|:---|:---|:---|
| Fix a site with no compression | Enable gzip or brotli at the server | 81.7% of the document, a median 184.3 KB |
| Decide whether to move gzip to brotli | Run the ledger first | Median 1.29 KB, negative on 31.4% of sites |
| Improve a site already on brotli | Confirm brotli beats its own gzip, then work on the document | Up to 105 KB on our worst case |
| Judge a fleet | Scan the header, then verify the bytes on the passes | Three requests per site |

**What to do this week:**

1. Run the ledger on your own homepage and write down the three numbers.
2. If row one carries a `Content-Encoding` header, discard it: your server ignores `identity`.
3. If brotli loses to gzip, raise the brotli quality level for HTML or serve gzip, and stop treating the header as a score.
4. Run the [free xSpeed Scan](https://xspeedcache.com/scan/) for D3 and the rest of the delivery dimension on every site you own, then read our [guide to checking whether WordPress caching is working](https://xspeedcache.com/blog/check-wordpress-caching-working/) for the checks beside it.
5. Managing more than five sites? [xSpeed Hub](https://xspeedcache.com/xspeed-hub/) runs those scans across a fleet from one connection. xSpeed Cache is free to start and Pro is $29 a year at the founding price with a 14-day money-back guarantee; the [Free vs Pro](https://xspeedcache.com/free-vs-pro/) page and the [80-capability comparison](https://xspeedcache.com/comparison/) show where the line falls.

The method behind this study is four curl invocations and a decompression check. We published the commands rather than the dataset because the useful artifact is the measurement you run on your own server. If your brotli is bigger than your gzip, we would like to know: that is a finding about a server default, and there are more of them than we expected.
